一般的なサブタイピングに混乱しています。
Java では、 typeA
が のサブタイプである場合B
、ジェネリック型C<A>
でありC<B>
、不変です。たとえば、ArrayList<Base>
は のサブタイプではありませんArrayList<Derived>
。
ただし、Scala では、ジェネリック型C<A>
とは、型が のサブタイプであるC<B>
場合、共変です。では、Scala にはあるが Java にはないジェネリック クラスのプロパティは何ですか?A
B
最初に、分散はジェネリック型パラメーターのプロパティであり、パラメーター化された型自体のプロパティではないことに注意してください。
第二に、あなたはscalaについて間違っています - 型パラメータはデフォルトで不変です。調べてみましょう!
Java には、ユース サイト分散アノテーションがあります。つまり、次のようにメソッドを宣言できます。
boolean addAll(Collection<? extends T> c);
ただし、型パラメーターが共変である「パラメーター化された型」(私はこの用語を大まかに使用しています) の 1 つの形式があります: Java 配列です! (配列は変更可能であり、型システムを簡単に回避できるため、これは実際には正気ではありません)。次の点を考慮してください。
public static void subvert(Object[] arr) { arr[0] = "Oh Noes!"; }
その後:
Integer[] arr = new Integer[1];
subvert(arr); //this call is allowed as arrays are covariant
Integer i = arr[0];
良いインタビューの質問:これはどうなりますか?
Scala では、宣言サイトの分散があります。つまり、型パラメーターのバリアンスは、パラメーターと一緒に宣言されます (注釈+
とを使用-
)。
trait Function1[-I, +O]
これは、トレイトFunction1
に 2 つの型パラメーターがI
あり、O
それぞれ反変と共変であることを示しています。注釈が宣言されていない場合+/-
、型パラメータは不変です。たとえば、 Set はその型パラメーターで不変です。
scala> def foo(set: Set[Any]) = ()
foo: (set: Set[Any])Unit
scala> Set(1)
res4: scala.collection.immutable.Set[Int] = Set(1)
scala> foo(res4)
<console>:10: error: type mismatch;
found : scala.collection.immutable.Set[Int]
required: Set[Any]
Note: Int <: Any, but trait Set is invariant in type A.
You may wish to investigate a wildcard type such as `_ <: Any`. (SLS 3.2.10)
foo(res4)
^
ただし、リストは共変として宣言されています。
scala> def bar(list: List[Any]) = ()
bar: (list: List[Any])Unit
scala> List(1)
res6: List[Int] = List(1)
scala> bar(res6)
これを実証する別の方法は、サブタイプの証拠をコンパイラに直接要求することです。
scala> class Cov[+A]
defined class Cov
scala> implicitly[Cov[Int] <:< Cov[Any]]
res8: <:<[Cov[Int],Cov[Any]] = <function1>
ただし、不変の型パラメーターを使用する
scala> class Inv[A]
defined class Inv
scala> implicitly[Inv[Int] <:< Inv[Any]]
<console>:9: error: Cannot prove that Inv[Int] <:< Inv[Any].
implicitly[Inv[Int] <:< Inv[Any]]
^
最後に、反変性:
scala> class Con[-A]
defined class Con
scala> implicitly[Con[Any] <:< Con[Int]]
res10: <:<[Con[Any],Con[Int]] = <function1>