Правила типов массивов и дженериков в Java

В последнем абзаце пункта 25 в «Эффективной Java» (2-й) сказано:

Массивы и дженерики имеют очень разные правила типов. Массивы ковариантны и материализованы; дженерики неизменны и стерты.

Может ли кто-нибудь дать более точное определение выделенных жирным шрифтом терминов в отношении массивов и дженериков в Java? Я бы тоже не отказался от примеров.


person flowjow    schedule 18.08.2015    source источник
comment
этот блог Эрика Липперта объясняет дисперсию , хотя это больше относится к С#. Вы пробовали википедию?   -  person D. Ben Knoble    schedule 18.08.2015
comment
Я думаю, что получу более четкий ответ на этот вопрос здесь, в SO. Я также думаю, что есть и другие люди, читающие эту прекрасную книгу, и у них может возникнуть такой же вопрос, как и у меня.   -  person flowjow    schedule 18.08.2015


Ответы (1)


Вы не упомянули, если вас запутала конкретная концепция, поэтому я попытаюсь просто дать основные определения ковариантности и инвариантности.

Ковариация сохраняет порядок типов, инвариантность — нет. Это означает, что подтипы сохраняются или не сохраняются (инвертируются в случае контравариантности).

Итак, если бы у вас был следующий класс

public class A {

  public void go() {

    System.out.println("A");
  }
}

а также...

public class B extends A {

  @Override
  public void go() {

    System.out.println("B");
  }
}

При ковариантной типизации (например, массивы) функция

public static void go(A[] as) {

  for (A a : as)
    a.go();
}

вполне допустимо использовать как

A[] as = new A[8];
B[] bs = new B[8];

go(as);
go(bs);

Другими словами, типы массивов доступны среде выполнения или овеществлены.

При инвариантной типизации (например, дженерики) подтипизация не сохраняется. Так, например, X<B> не будет иметь отношения типа к X<A>, кроме X. Частично это является следствием того, что универсальные типы не отображаются во время выполнения или стираются.

Однако вы все равно можете явно выразить ковариантность и контравариантность в Java, используя extends и super соответственно. Например с классом

public class X<T extends A> {

  private T t_;

  public X(T t) {
   t_ = t;
  }

  public void go() {

    t_.go();
  }
}

Функция

public static void go(X<?> x) {

  x.go();
}

будет действителен в качестве

X<A> xa = new X<A>(a);
X<B> xb = new X<B>(b);

go(xa);
go(xb);
person Jason    schedule 18.08.2015