【问题标题】:Possible to find parameter type methods return type in Scala where parameter is a primitive type?可以在 Scala 中找到参数类型方法返回类型,其中参数是原始类型?
【发布时间】:2015-01-09 22:52:28
【问题描述】:

假设我有:

class X
{
   val listPrimitive: List[Int] = null
   val listX: List[X] = null
}

我打印出Scala中每个方法的返回类型如下:

classOf[ComplexType].getMethods().foreach { m => println(s"${m.getName}: ${m.getGenericReturnType()}") }

listPrimitive: scala.collection.immutable.List<Object> 
listX: scala.collection.immutable.List<X> 

所以...我可以确定listX 的元素类型是X,但是有没有办法通过反射确定listPrimitive 的元素类型实际上是java.lang.Integer? ...

val list:List[Int] = List[Int](123);
val listErased:List[_] = list;
println(s"${listErased(0).getClass()}")  // java.lang.Integer

注意。由于 JVM 类型擦除,这似乎不是问题,因为我 可以 找到 List 的 types 参数。看起来scala编译器丢弃了这个类型信息IFF参数类型是 java.lang.[numbers] 。

更新:

由于以下实验,我怀疑此类型信息可用的。假设我定义:

class TestX{
  def f(x:X):Unit = {
    val floats:List[Float] = x.listPrimitive()  // type mismatch error
  }
}

并且 X.class 是通过 jar 导入的。完整的类型信息必须在 X.class 中可用,这样这种情况才能正确编译失败。

更新2:

假设您正在为 Java 序列化库编写 scala 扩展。你需要实现一个:

def getSerializer(clz:Class[_]):Serializer

根据是否需要做不同事情的函数:

clz==List[Int]   (or equivalently: List[java.lang.Integer])
clz==List[Float] (or equivalently: List[java.lang.Float])
clz==List[MyClass]

我的问题是我只会看到:

clz==List[Object]
clz==List[Object]
clz==List[MyClass]

因为 clz 以 clz.getMethods()(i).getGenericReturnType() 的形式提供给此函数。

从 clz:Class[_] 开始,如何恢复丢失的元素类型信息?

我不清楚 TypeToken 是否会对我有所帮助,因为它的用途:

typeTag[T]

要求我提供 T(即在编译时)。

所以,一个解决方案的路径...给定一些 clz:Class[_],我可以确定其方法返回类型的 TypeTokens 吗?显然,这是可能的,因为此信息必须包含在 .class 文件中(某处),以便 scala 编译器正确生成类型不匹配错误(见上文)。

【问题讨论】:

    标签: scala reflection scala-reflect


    【解决方案1】:

    在 java 字节码级别,Ints 必须表示为其他东西(显然是 Object),因为 List 只能包含对象,不能包含原语。这就是java级反射可以告诉你的。但是,正如您推断的那样,scala 类型信息是存在的(在字节码级别,它位于注释 IIRC 中),因此您应该能够使用 scala 反射检查它:

    import scala.reflect.runtime.universe._
    
    val list:List[Int] = List[Int](123)
    
    def printTypeOf[A: TypeTag](a: A) = println(typeOf[A])
    
    printTypeOf(list)
    

    对 update2 的响应:您应该使用 scala 反射来获取镜像,而不是 Class[_] 对象。如果需要,您可以通过类名:

    import scala.reflect.runtime.universe._
    
    val rm = runtimeMirror(getClass.getClassLoader)
    
    val someClass: Class[_] = ...
    
    val scalaMirrorOfClass = rm.staticClass(someClass.getName)
    // or possibly rm.reflectClass(someClass) ?
    
    val someObject: Any = ...
    
    val scalaMirrorOfObject = rm.reflectClass(someObject)
    

    我猜如果你真的只有这个类,你可以创建一个只加载那个类的类加载器吗?不过,我无法想象没有类,甚至没有值的用例。

    【讨论】:

    • 对象表示为 java.lang.Integer,当然。但是 TypeTag 不仅仅适用于对象和类型。我正在寻找方法的返回类型(编译器清楚地知道并且在类文件中的某个地方可用)。
    • 发现元素类型是 Int(而不是 java.lang.Integer)也可以解决我的问题。但是,也缺少此信息。
    • 我不明白。 TypeTag 只是获取类型的助手;如果您使用 scala 反射检查方法返回类型,您可以获得与 typeOf 相同的类型表示。
    • typeOf 没有帮助,因为它需要一个 [T] 类型参数,而我没有。我从 Class[_] 开始,需要从类的方法的返回类型中退出 TypeTag。如何才能做到这一点? (我在上面添加了 UPDATE2)。
    • mirror.classSymbol(clz).toType 让我找到完整的信息。谢谢!
    猜你喜欢
    • 1970-01-01
    • 2016-12-08
    • 2019-01-19
    • 1970-01-01
    • 2015-12-14
    • 1970-01-01
    • 2018-08-10
    • 2016-08-15
    • 1970-01-01
    相关资源
    最近更新 更多