为什么Scala不会自动“处理”ClassTags?

时间:2014-02-10 17:02:39

标签: scala reflection

类标记似乎非常容易使用,但是,在数学上是否可以始终可靠地自动插入ClassTag样板至少在某些情况下(如果不是全部的话)?考虑这个例子:

def foo[T: ClassTag] = {
    val cls = classTag[T].runtimeClass.asInstanceOf[Class[T]]
    val myList: Seq[T] = parseList(rawJson, cls) // let's assume parseList a library method that needs to work with a Class instance (e.g. RestFB's fetchConnnection)
    ...
}

为什么Scala不会让我改写:

def foo[T] = {
    val cls = classOf[T]
    val myList: Seq[T] = parseList(rawJson, cls) // let's assume parseList a library method that needs to work with a Class instance (e.g. RestFB's fetchConnnection)
    ...
}

...并自动将后者转换为前者?

是否有技术原因,例如编译器/反射库并不总能可靠地确定类标签的可取性?或者纯粹是一种有意识的设计选择来保持这一点?例如,鼓励程序员首先避免使用类标签以获得更清洁的设计和更好的性能,即使已知在许多情况下它是不可能的?

PS 问为什么Scala没有(也不建议应该)将ClassTag魔法添加到每个带有泛型类型参数的方法定义;我只是问为什么当需要/不可避免时它无法自动完成。

1 个答案:

答案 0 :(得分:4)

此语法:

def foo[T: ClassTag] = ...

实际上是这个的简写:

def foo[T](implicit ev: ClassTag[T]) = ...

这意味着如果为每个泛型类型参数提供ClassTag[T],那么每个函数都会有额外的隐藏参数,甚至几个。

这当然是完全不受欢迎的。这种方法最突出的缺点之一是使用泛型时几乎破坏了Java的互操作性:

SomeClass someClass = new SomeClass();
someClass.foo<Integer>(ClassTag.apply(Integer.class));

想象一下更高级的通用方法和参数。这只是为了能够匹配泛型类型,这几乎是不需要的。

调用Java泛型方法也是不明智的。这里应该调用哪种方法:

val jc = new JavaClass
jc.foo[Int]()

如果JavaClass定义如下:

public class JavaClass {
    public <T> void foo() { ... }
    public <T> void foo(ClassTag<T> ct) { ... }
}

当然,如果Java支持reified generics,一切都会有所不同......