John Threepwood
John Threepwood

Reputation: 16143

How to determine subtype of a type parameter in Scala?

class S
class A extends S
class B extends S

class ClassWithTypeParameter[+T]

val a: ClassWithTypeParameter[S] = new ClassWithTypeParameter[A]

How can one determine the type of the subclass used for the type parameter of value a ?

Upvotes: 2

Views: 290

Answers (2)

Travis Brown
Travis Brown

Reputation: 139038

The following is a bad idea (like most uses of reflection) but it works:

class ClassWithTypeParameter[+T: Manifest] {
  def paramIs[V: Manifest] = manifest[T] == manifest[V]
}

Which gives us:

scala> val a: ClassWithTypeParameter[S] = new ClassWithTypeParameter[A]
a: ClassWithTypeParameter[S] = ClassWithTypeParameter@6493c09c

scala> a.paramIs[A]
res0: Boolean = true

scala> a.paramIs[S]
res1: Boolean = false

scala> a.paramIs[B]
res2: Boolean = false

And:

scala> val i = new ClassWithTypeParameter[List[Int]]
i: ClassWithTypeParameter[List[Int]] = ClassWithTypeParameter@260702ee

scala> i.paramIs[List[Int]]
res3: Boolean = true

scala> i.paramIs[List[Double]]
res4: Boolean = false

You could write similar paramSubtypeOf and paramSupertypeOf methods using Manifest's <:< and >:>.

In Scala 2.10 (Milestone 4 or later) there's a much more flexible way to get the type:

class ClassWithTypeParameter[+T: TypeTag] {
  def paramType = reflect.runtime.universe.typeTag[T].tpe
}

Now you could write things like a.paramType.parents to get the immediate supertypes, etc.

Upvotes: 2

oxbow_lakes
oxbow_lakes

Reputation: 134270

You cannot because of type erasure. However, I would argue that your attempt to do this is formed from a mis-understanding.

  • The point of a type system is so that the compiler can reason more powerfully about the correctness of your program.
  • In a static type system, each reference has a type which cannot be changed

In your program, there is one reference, a and the type of this reference is ClassWithTypeParameter[S]. That. Is. All. The compiler can know what can be done with this reference. The types are there purely for the compiler. The fact that, at runtime, a was assigned to a value which was a ClassWithTypeParameter[A] is irrelevant.


One possible way of doing this to some approximation (limited by erasure) is to use manifests (called something else in 2.10):

class ClassWithTypeParameter[+T: Manifest] { def erasure = manifest[T].erasure }

Then you can call erasure which will get you a java.lang.Class back. As I said, this is limited. A class is not the same thing as a type and there is no way to distinguish, for example, ClassWithTypeParameter[List[Int]] from ClassWithTypeParameter[List[Double]]

Upvotes: 3

Related Questions