Is it possible to create a Class object purely from a generic parameter? For example:
class myclass[T] {
def something(): Class[_ <: T] =
classOf[T] //this doesn't work
}
Since the type will have been erased at runtime, it seems like this a job for manifests, but I haven't found an example that demonstrates this particular usage. I tried the following, but it doesn't work either:
class myclass[T] {
def something()(implicit m: Manifest[T]): Class[_ <: T] =
m.erasure //this doesn't work
}
I suspect this failure is due to, as the API points out, there is no subtype relationship between the type of m.erasure
's result and T
.
EDIT: I'm not really interested in what the type T
is, I just need an object of type Class[_ <: T]
to pass to a method in the hadoop framework.
Any pointers?
def myClassOf[T:ClassTag] = implicitly[ClassTag[T]].runtimeClass
You can cast the result of m.erasure
to a Class[T]
:
class myclass[T] {
def something()(implicit m: Manifest[T]): Class[T] =
m.erasure.asInstanceOf[Class[T]]
}
This works fine for basic (non-generic) types:
scala> new myclass[String]().something()
res5: Class[String] = class java.lang.String
But note what happens if I use an instantiated type constructor like List[String]
for T
:
scala> new myclass[List[String]]().something()
res6: Class[List[String]] = class scala.collection.immutable.List
Due to erasure, there is only one Class
object for all the possible instantiations of a given type constructor.
I'm not sure why Manifest[T].erasure
returns Class[_]
instead of Class[T]
, but if I had to speculate, I would say it's to discourage you from using the methods on Class
which allow you to compare two classes for equality or a subtype relationship, since those methods will give you wrong answers when the Class
is parameterized with an instantiated generic type.
For example,
scala> classOf[List[String]] == classOf[List[Int]]
res25: Boolean = true
scala> classOf[List[String]].isAssignableFrom(classOf[List[Int]])
res26: Boolean = true
These results might surprise you and/or lead to a bug in your program. Instead of comparing classes this way, you should normally just pass around Manifest
s instead and compare them, since they have more information*:
scala> manifest[List[String]] == manifest[List[Int]]
res27: Boolean = false
scala> manifest[List[String]] >:> manifest[List[Int]]
res28: Boolean = false
As I understand it, Manifest
s are meant to supersede Class
es for most use cases... but of course, if you're using a framework that requires a Class
, there's not much choice. I would suppose that the imposition of casting the result of erasure
is just a sort of "acknowledgement of liability" that you're using an inferior product at your own risk :)
* Note that, as the documentation for Manifest says, these manifest comparison operators "should be considered approximations only, as there are numerous aspects of type conformance which are not yet adequately represented in manifests."
.erasure
gives you the type to which your type erases to. If you want a full type information, you should return the Manifest
instead.
scala> class MyClass[A] {
| def stuff(implicit m: Manifest[A]): Class[_] = m.erasure
| }
defined class MyClass
scala> new MyClass[Int].stuff
res551: java.lang.Class[_] = int
scala> new MyClass[List[Int]].stuff
res552: java.lang.Class[_] = class scala.collection.immutable.List
scala> class MyClass[A] {
| def stuff(implicit m: Manifest[A]): Manifest[A] = m
| }
defined class MyClass
scala> new MyClass[Int].stuff
res553: Manifest[Int] = Int
scala> new MyClass[List[Int]].stuff
res554: Manifest[List[Int]] = scala.collection.immutable.List[Int]
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.