关于反射:为什么 Scala 不自动”处理”ClassTags?

Why doesn't Scala "handle" ClassTags automatically?

Class 标签似乎真的很容易使用,但是,至少在某些情况下(如果不是全部)总是可靠地自动插入 ClassTag 样板在数学上是不可能的吗?考虑这个例子:

1
2
3
4
5
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 不让我改写:

1
2
3
4
5
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)
    ...
}

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

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

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


这个语法:

1
def foo[T: ClassTag] = ...

其实是这个的简写:

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

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

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

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

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

调用 Java 泛型方法也是模棱两可的。这里应该调用哪个方法:

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

如果 JavaClass 是这样定义的:

1
2
3
4
public class JavaClass {
    public < T > void foo() { ... }
    public < T > void foo(ClassTag< T > ct) { ... }
}

?

当然,如果 Java 支持物化泛型,一切都会不同...