【发布时间】:2013-01-26 18:25:38
【问题描述】:
Java 泛型可以根据表达式的返回类型推断泛型类型参数的类型。考虑以下几点:
public static <T> T uncheckedCast(Object o) {
return (T)o;
}
我们可以这样称呼它:
Map<Baz,Bog> bazbogMap = new HashMap<Baz,Bog>();
String foo = uncheckedCast(bazbogMap);
这将编译,但在调用时抛出 RuntimeException,因为它会尝试将 Map 转换为 String 但失败。但重点是 Java 根据调用点的预期结果类型推断出 <T> 的值。
我们也可以在 Scala 中这样做:
def uncheckedCast[T](o: AnyRef): T = o.asInstanceOf[T]
到目前为止,一切都很好。
Java 还可以从嵌套定义中推断类型参数并返回它们(即,在使用它之前,我们实际上不必将结果分配给一个类型来使用它;Java 知道它已经是什么。)
一个简单的类显示这个:
import java.util.HashMap;
import java.util.Map;
public class KeysAndValues {
public interface Key<T> {}
public static class StringKey implements Key<String> {}
private final Map<Class<?>, Object> lookup;
public KeysAndValues() {
lookup = new HashMap<Class<?>, Object>();
}
@SuppressWarnings("unchecked")
public <V, K extends Key<V>> V put(Class<K> key, V value) {
return (V) lookup.put(key, value);
}
@SuppressWarnings("unchecked")
public <V, K extends Key<V>> V get(Class<K> key) {
return (V) lookup.get(key);
}
public static void test() {
KeysAndValues kv = new KeysAndValues();
kv.put(StringKey.class, "BAM!"); // returns null
kv.put(StringKey.class, "BOOM!"); // returns "BAM!"
kv.get(StringKey.class); // returns "BOOM!"
}
}
但是,在 Scala 中,此类会导致问题。 REPL:
scala> val kv = new KeysAndValues
kv: KeysAndValues = KeysAndValues@13c2982e
scala> import KeysAndValues.StringKey
import KeysAndValues.StringKey
scala> kv.put(classOf[StringKey], "BAM!")
res0: java.lang.String = null
scala> kv.put(classOf[StringKey], "BOOM!")
res1: java.lang.String = BAM!
scala> kv.get(classOf[StringKey])
<console>:10: error: inferred type arguments [Nothing,KeysAndValues.StringKey] do not conform to method get's type parameter bounds [V,K <: KeysAndValues.Key[V]]
kv.get(classOf[StringKey])
对put() 的两次调用为V 类型参数指定了一个值,该值允许它们工作,但是一旦需要从K 参数的继承中提取V 参数,事情就会发生分解。 Java 代码没有这样的问题。
让 Scala 不抱怨的唯一方法是明确定义类型:
kv.get[String, StringKey](classOf[StringKey])
(对于像上面这样简单和做作的事情,它有点好(违反 DRY),但对于更复杂的事情,比如斯坦福核心 NLP API,你必须做类似的事情:
doc.get[java.util.Map[Integer, CorefChain], CorefChainAnnotation](classOf[CorefChainAnnotation])
其中包括手动查找嵌套类型以便添加它们,这非常痛苦。)
问题:
有没有办法让它工作而不必如此冗长地指定类型参数?更重要的是,为什么这是一个问题?当 Scala 不是时,Java 如何能够推断类型参数?
编辑 1:删除了大部分演示斯坦福核心 NLP 问题的代码,并将其替换为 Scala/Java 泛型差异的一般示例,以说明该问题。
【问题讨论】:
-
你能在没有外部库的情况下用可编译的纯 Java 和 Scala 代码重现你的问题吗?
-
可能。让我试一试。
-
添加了一个 Java 类和显示差异的 REPL 输出。
标签: java scala generics stanford-nlp scala-java-interop