抽象类型T是未经检查的,因为它被擦除消除了。

3
我正在编写一个扩展Java类的Scala类。我必须继承这个抽象Java类,因为它包含一些转换器,并且我需要编写自己的转换器,以便将其插入到他们的框架中。因此,下面的方法签名被强制执行:当我说我的类扩展了他们的抽象类时,Eclipse会自动生成它。如果我尝试修改方法签名,则类无法编译,因为“它没有实现来自父类的抽象方法”。
当然,方法的实现是我自己选择的 - 但我发现我无法满足类型系统的约束条件。
我需要做一些非常简单的事情:将“None”视为null,并返回其他任何值。但我发现我无法以类型安全的方式做到这一点。如果我像下面显示的代码一样编写代码,我会收到警告,指出“未检查的抽象类型T,因为它被消除了”。我该如何消除这种警告?
我也想知道一旦擦除应用于它,即第二个“if”条件会是什么样子。
直观地讲,我明白当输入为Any时我不能保证返回一个T - 这就是为什么编译器不高兴的原因。实际上,这将起作用,因为我们只是读取字符串并返回字符串 - 但方法签名是由继承声明强制执行的。
  def execute[T](value: Any): T = {
    if (value == null || value == "None") null.asInstanceOf[T]
    // Warning: abstract type T is unchecked since it is eliminated by erasure
    else if (value.isInstanceOf[T]) value.asInstanceOf[T]
    //            warning here: ^
    else null.asInstanceOf[T]
  }

我该如何实现这个方法?


1
也许这可以帮助你:https://dev59.com/CHNA5IYBdhLWcg3wF5yO。 - paulotorrens
由于擦除将第二个“if”语句转换为“isInstanceOf [Object]”,这总是为真,因此实际上保证不会做程序员想要的事情。我很震惊这只是一个警告而不是错误。我意识到这个问题无法以类型安全的方式解决,因为输入是任何内容,输出是T。从Alexey的解决方案中我认识到,一旦你开始做这样的事情,你基本上是在告诉编译器“相信我,在运行时它将被转换为T”,并且编译器的“类型安全保证”变成了“我看到程序员承诺了一个T”。 - radumanolescu
2个回答

2
您的Java解决方案不包含测试value instanceof T,因此在Scala解决方案中不需要isInstanceOf[T]。您应该将其删除,因为该检查不起作用:它被擦除为isInstanceOf[Object],这始终为真,因此永远不会执行最后一个else分支。剩下的区别是,在Java中,您正在比较String.valueOf(value)"None",而不是value本身。因此,Java解决方案的等效方式为:
def execute[T](value: Any): T = {
  (if (value == null || String.valueOf(value) == "None") null else value).asInstanceOf[T]
}

根据String.valueOf的定义,条件可以简化为value == null || value.toString == "None"
我认为编译器应该对asInstanceOf[T]发出警告,而不仅仅是对isInstanceOf[T]发出警告。在这种情况下,Java实际上比Scala更安全,因为1)它确实对转换发出警告;2)instanceof检查将是一个错误而不是一个警告。
顺便说一句,虽然你可以消除警告,但是具有像这样签名的方法强烈表明作者没有足够理解Java泛型,我会非常谨慎地依赖于那个库。

1
最终,我可以做两件事情之一:
1. 用Java实现: @SuppressWarnings("unchecked") @Override public T execute(Object value) { if (value == null || "None".equals(String.valueOf(value))) return null; else return (T) value; }
2. 用Scala实现并添加@unchecked def execute[T](value: Any): T = { if (value == null || value == "None") null.asInstanceOf[T] else if (value.isInstanceOf[T @unchecked]) value.asInstanceOf[T] else null.asInstanceOf[T] }
我仍然想知道擦除后第二个if语句剩下什么。
感谢Alec的建议!

这样做符合您的要求吗?你所做的只是抑制警告... - Alec
擦除仍然会让你受挫。如果您无法添加“ClassTag”或“Manifest”,则可能需要使用Java实现。 - JRomero
如果我查看Java代码,我确定它会按照我的意愿执行,因为我理解它的功能。几乎没有任何实现依赖于T。最后的转换可以忽略,我仍然可以得到我想要的结果。在Scala中,我不知道类型擦除后第二个if语句会变成什么样子。我需要进行测试。 - radumanolescu
由于我已经删除了我的答案(因为它不起作用,因为 OP 无法添加 ClassTagManifest),在这里是未来参考的代码:def execute[T: ClassTag](value: Any): T = value match { case "None" => null.asInstanceOf[T] case x: T => x case _ => null.asInstanceOf[T] } - Alec

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接