java - Scala 和 Java 泛型——提取和返回嵌套类型

标签 java scala generics stanford-nlp scala-java-interop

Java泛型可以根据表达式的返回类型推断出泛型类型参数的类型。请考虑以下事项:

public static <T> T uncheckedCast(Object o) {
    return (T)o;
}

我们可以这样调用它:

Map<Baz,Bog> bazbogMap = new HashMap<Baz,Bog>();
String foo = uncheckedCast(bazbogMap);

这将编译但抛出 RuntimeException当它被调用时,因为它会尝试转换 MapString但失败了。但重点是 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 中,此类会导致问题。回复:

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 指定一个值允许它们工作的类型参数,但一旦 V需要从 K 的继承中提取参数参数,事情崩溃了。 Java代码没有这样的问题。

让 Scala 不提示的唯一方法是显式定义类型:

kv.get[String, StringKey](classOf[StringKey])

(对于像上面这样的简单和人为的事情,它还可以(违反 DRY),但对于更复杂的事情,比如 Stanford Core NLP API,你必须做这样的事情:

doc.get[java.util.Map[Integer, CorefChain], CorefChainAnnotation](classOf[CorefChainAnnotation])

其中包括手动查找嵌套类型以便您可以添加它们,这非常痛苦。)

问题:

有没有办法让它工作而不必如此冗长地指定类型参数?更重要的是,为什么这是一个问题?当 Scala 不能时,Java 如何能够推断类型参数?

编辑 1:删除了大部分演示 Stanford Core NLP 问题的代码,并将其替换为说明该问题的 Scala/Java 泛型差异的一般示例。

最佳答案

我不能确切地告诉你为什么 Scala 不能推断出 Java 类型参数,但我至少可以告诉你如何在 Java 中声明类型参数以便 Scala 可以 推断它们:

@SuppressWarnings("unchecked")
public <V> V put(Class<? extends Key<V>> key, V value) {
    return (V) lookup.put(key, value);
}

@SuppressWarnings("unchecked")
public <V> V get(Class<? extends Key<V>> key) {
    return (V) lookup.get(key);
}

我认为这里的关键点是您不需要 K完全类型参数 - 通配类型参数,? extends Key<V>将为您提供相同的功能。然后,这可以在 Scala 中愉快地工作,而无需指定任何类型参数:

scala> kv.put(classOf[StringKey], "BAM!")
res0: String = null

scala> kv.get(classOf[StringKey])
res1: String = BAM!

编辑:

这是我能做的最好的解决方法。它需要更高级的类型和类型转换。不确定我能解释为什么这个编译但另一个版本没有。 ;-)

scala> import KeysAndValues._
import KeysAndValues._

scala> import scala.language.higherKinds
import scala.language.higherKinds

scala> def get[V, K[X] <: Key[X]](kv: KeysAndValues, key: Class[_ <: K[V]]) =
     |   kv.get[V, K[V]](key.asInstanceOf[Class[K[V]]])
get: [V, K[X] <: KeysAndValues.Key[X]](kv: KeysAndValues, key: Class[_ <: K[V]])V

scala> val kv = new KeysAndValues
kv: KeysAndValues = KeysAndValues@24c63dac

scala> kv.put(classOf[StringKey], "BAM!")
res0: String = null

scala> get(kv, classOf[StringKey])
res1: String = BAM!

关于java - Scala 和 Java 泛型——提取和返回嵌套类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14815751/

相关文章:

java - java中只使用key对象的引用的缓存(而不是hashCode或equals)

java - 当使用参数化运行器运行时,如何让 JUnit 在运行时忽略特定的测试用例?

json - Scala play!2 为外部库中的类创建 Json 格式化程序

scala - 方法迭代器和 View 之间有什么区别?

java - 如何在循环中均匀分布整数?

Scala:用字符串reduceLeft

java泛型,未经检查的警告

c# - 泛型类型推断在 C# 中如何工作?

swift - 有没有一些方法可以检查类是否是从泛型类型继承的?

java - 使用抽象方法时定义的 var 为 null