Reputation: 16143
Is it possible to write a method in Scala which returns an object of a type-parameterized class with different type paramter ? Something like this:
class A[T]
def f(switch: Boolean): A = if(switch) new A[Int] else new A[String]
Please note: The Code above is fictional to show the type of problem; The code above does not make semantically sense.
The code above will not compile because return type A is not parameterized.
Upvotes: 3
Views: 2625
Reputation: 155
How about an absolutely minimal change to the "fictional code"? If we just add [_]
after the "fictional" return type, the code will compile:
class A[T]
def f(switch: Boolean):A[_] = if(switch) new A[Int] else new A[String]
It is worth noting that A[_]
is not the same as A[Any]
. A[T]
does not need to be defined covariant for the code to compile.
Unfortunately, information about the type gets lost.
Upvotes: -1
Reputation: 134270
One way of expressing this would be by using Either
;
def f(switch: Boolean) = if (switch) Left(new A[Int]) else Right(newA[String])
This of course returns an Either[A[Int], A[String]]
. You certainly cannot (at the moment) declare a method which returns some parameterized type P
, with some subset of type parameters (i.e. only Int
or String
).
The language ceylon has union types and I understand the intention is to add these to scala in the near future, in which case, you could define a method:
def f(switch: Boolean): A[Int|String] = ...
Upvotes: 4
Reputation: 92026
Well, you could do something like that.
scala> class A {
| type T
| }
defined class A
scala> def f(b: Boolean): A = if(b) new A { type T = Int } else new A { type T = String }
f: (b: Boolean)A
But this is pointless. Types are a compile time information, and that information is getting lost here.
Upvotes: 0
Reputation: 167871
You can, and you can even do it with type-safety with the aid of implicit arguments that encapsulate the pairings:
class TypeMapping[+A,B] {
def newListB = List.empty[B]
}
trait Logical
object True extends Logical
object False extends Logical
implicit val mapFalseToInt = new TypeMapping[False.type,Int]
implicit val mapTrueToString = new TypeMapping[True.type,String]
def f[A <: Logical,B](switch: A)(implicit tmap: TypeMapping[A,B]) = tmap.newListB
scala> f(True)
res2: List[String] = List()
scala> f(False)
res3: List[Int] = List()
You do have to explicitly map from boolean values to the custom True
and False
values.
(I have chosen List
as the target class just as an example; you could pick anything or even make it generic with a little more work.)
(Edit: as oxbow_lakes points out, if you need all possible return values to be represented on the same code path, then this alone won't do it, because the superclass of List[Int]
and List[String]
is List[Any]
, which isn't much help. In that case, you should use an Either
. My solution is for a single function that will be used only in the True
or False
contexts, and can maintain the type information there.)
Upvotes: 6