Scala - 从泛型类型获取类对象

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8208179/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 03:41:27  来源:igfitidea点击:

Scala - obtaining a class object from a generic type

genericsscalamanifest

提问by dhg

Is it possible to create a Class object purely from a generic parameter? For example:

是否可以仅从泛型参数创建 Class 对象?例如:

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.

我怀疑这种失败是由于,正如 API 指出的那样,m.erasure的结果类型和T.

EDIT: I'm not really interested in what the type Tis, I just need an object of type Class[_ <: T]to pass to a method in the hadoop framework.

编辑:我对类型不是很感兴趣T,我只需要一个类型的对象Class[_ <: T]来传递给 hadoop 框架中的方法。

Any pointers?

任何指针?

采纳答案by Tom Crockett

You can cast the result of m.erasureto a Class[T]:

您可以将结果转换m.erasure为 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:

但请注意,如果我使用实例化类型构造函数,例如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 Classobject for all the possible instantiations of a given type constructor.

由于擦除,Class给定类型构造函数的所有可能实例化只有一个对象。

Edit

编辑

I'm not sure why Manifest[T].erasurereturns Class[_]instead of Class[T], but if I had to speculate, I would say it's to discourage you from using the methods on Classwhich allow you to compare two classes for equality or a subtype relationship, since those methods will give you wrong answers when the Classis parameterized with an instantiated generic type.

我不确定为什么Manifest[T].erasure返回Class[_]而不是Class[T],但如果我不得不推测,我会说这是为了阻止您使用Class允许您比较两个类的相等性或子类型关系的方法,因为这些方法会给你错误当Class用实例化的泛型类型参数化时回答。

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 Manifests instead and compare them, since they have more information*:

这些结果可能会让您感到惊讶和/或导致您的程序出现错误。不要以这种方式比较类,您通常应该只传递Manifests 并比较它们,因为它们有更多信息*:

scala> manifest[List[String]] == manifest[List[Int]]
res27: Boolean = false

scala> manifest[List[String]] >:> manifest[List[Int]]
res28: Boolean = false

As I understand it, Manifests are meant to supersede Classes 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 erasureis just a sort of "acknowledgement of liability" that you're using an inferior product at your own risk :)

据我了解,对于大多数用例,Manifests 旨在取代Classes ……但是当然,如​​果您使用的框架需要 a Class,则没有太多选择。我认为强制转换结果erasure只是一种“承认责任”,您使用劣质产品的风险由您自己承担:)

* Note that, as the documentation for Manifestsays, 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."

* 请注意,正如Manifest文档所说,这些清单比较运算符“应仅被视为近似值,因为类型一致性的许多方面尚未在清单中充分表示。”

回答by Ali Salehi

def myClassOf[T:ClassTag] = implicitly[ClassTag[T]].runtimeClass

回答by missingfaktor

.erasuregives you the type to which your type erases to. If you want a full type information, you should return the Manifestinstead.

.erasure给你你的类型擦除的类型。如果你想要一个完整的类型信息,你应该返回Manifest代替。

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]