关于反射:为什么 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 支持物化泛型,一切都会不同…

  • “为每个泛型类型参数提供”——但我没有说转换应该每次都发生……只有当它\\是不可避免的时候。
  • @ErikAllik,这意味着非常不稳定的 API。假设您更改了以前不需要 ClassTag 的代码,因此它开始依赖它。然后,您的 API 的所有用户都可能会中断,而您甚至都不会注意到。当您的泛型方法覆盖另一个方法时,您甚至可以破坏自己的代码 – 突然它将停止覆盖其父方法。此外,这意味着您无法完全控制方法的签名。我当然不喜欢那样。
  • 好的,这是一个好点;我会再等一会儿,看看其他人是否能提出一些额外的理由。


来源:https://www.codenong.com/21683420/

微信公众号
手机浏览(小程序)
0
分享到:
没有账号? 忘记密码?