Scala - 从泛型类型中获取类对象 [英] Scala - obtaining a class object from a generic type

查看:335
本文介绍了Scala - 从泛型类型中获取类对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否可以纯粹从泛型参数创建一个Class对象?例如:

  class myclass [T] {
def something():Class [_<:T] =
classOf [T] //这不起作用
}

由于这个类型在运行时会被擦除,看起来这是一个清单工作,但我还没找到一个演示这种特殊用法的例子。我尝试了以下,但它也不起作用:

  class myclass [T] {
def something( )(implicit m:Manifest [T]):Class [_<:T] =
m.erasure //这不起作用
}

我怀疑这是由于API指出, m.erasure类型之间没有子类型关系的结果和 T



编辑:我对什么类型的 T 是,我只需要一个类型为 Class [_<:T] 的对象来传递给一个在hadoop框架中的方法。

任何指针?

解决方案

您可以将 m.erasure 的结果转换为 Class [T]

  class myclass [T] {
def something()(implicit m:Manifest [T]):Class [T] =
m .erasure.asInstanceOf [Class [T]]
}

非转基因neric)类型:

  scala> new myclass [String]()。something()
res5:Class [String] = class java.lang.String

但是请注意,如果我为 T List [String] 这样的实例化类型构造函数会发生什么>:

  scala> new myclass [List [String]]()。something()
res6:Class [List [String]] = class scala.collection.immutable.List

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

编辑



我不确定为什么 Manifest [T] .erasure 返回 Class [_] 而不是 Class [T] ,但是如果我必须推测,我会说这是为了阻止你使用 Class 上的方法,它们允许你比较两个类是否相等或是一个子类型关系,因为这些方法在 Class 使用实例化的泛型类型进行参数化。

例如,

  scala> classOf [List [String]] == classOf [List [Int]] 
res25:Boolean = true

scala> classOf [List [String]]。isAssignableFrom(classOf [List [Int]])
res26:Boolean = true

这些结果可能会让您感到惊讶,并且/或者导致程序中出现错误。而不是像这样比较类,你通常应该传递 Manifest s来比较它们,因为它们有更多的信息*:

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

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

据我所知,对于大多数用例, Manifest s意味着取代 Class es ......但是当然,如​​果您正在使用一个需要 Class 的框架,没有多少选择。我假设强制转换擦除结果的结果只是一种承担责任,即您使用劣质产品时需要您自担风险:



*请注意,正如文件清单中说,这些清单比较操作符应该只被认为是近似值,因为类型一致性的许多方面尚未在清单中充分表示。

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?

解决方案

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.

Edit

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 Manifests 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, 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 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."

这篇关于Scala - 从泛型类型中获取类对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆