Подтвердить что ты не робот

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

У меня есть следующий код:

public static void main(String[] args) {
    List<String> s = new ArrayList<String>();
    s.add("kshitiz");

    //This is not typesafe. It should blow up at runtime
    List<Integer> i = new ArrayList(s);
    System.out.println(i.get(0));
}

Эта программа работает нормально и печатает kshitiz. Он не работает, если я заменю последнюю строку:

System.out.println(i.get(0).getClass());

Исключение:

Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer

Что здесь происходит?

4b9b3361

Ответ 1

Я предполагаю, что вам известно, что во время выполнения генерические типы отсутствуют. Теперь, чтобы посмотреть, что происходит за кулисами, рассмотрим этот фрагмент кода

public static Class<?> getClass(final Object object) {
    return object.getClass();
}

public static void main(final String[] args) {
    final List<String> s = new ArrayList<String>();
    s.add("kshitiz");

    // This is not typesafe. It should blow up at runtime
    final List<Integer> i = new ArrayList(s);
    System.out.println(getClass(i.get(0)));
    System.out.println(i.get(0).getClass());
}

а выход javap -c

  [...]
  26: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
  29: aload_2
  30: iconst_0
  31: invokeinterface #9,  2            // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
  36: invokestatic  #10                 // Method getClass:(Ljava/lang/Object;)Ljava/lang/Class;
  39: invokevirtual #11                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
  42: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
  45: aload_2
  46: iconst_0
  47: invokeinterface #9,  2            // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
  52: checkcast     #12                 // class java/lang/Integer
  55: invokevirtual #2                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
  58: invokevirtual #11                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
  61: return

Итак, вы видите, что во втором вызове String передается в Integer, в то время как в первом случае он рассматривается как объект. Таким образом, пока String обрабатывается так же, как и любой другой объект, все в порядке, но как только вы вызываете метод типа элемента списка, объект передается этому типу.

Ответ 2

Дженерики в java разбиты по дизайну.

В вашем List<Integer> i = new ArrayList(s); у вас есть raw ArrayList. Это означает, что вы игнорируете параметр типа, и он работает, как если бы он был Object. Затем вы (неявно) передаете ArrayList в List<Integer>, но generics не проверяются во время выполнения, поэтому JVM не заботится о несоответствии.

Когда вы выполняете System.out.println(i.get(0)), вы не конвертируете String в Integer, потому что вызывается метод println (Object) `.

В System.out.println(i.get(0).getClass()) эта магия тормозит, потому что по какой-то причине (вы, вероятно, можете найти эту причину в спецификациях, но я предлагаю вам не делать этого), ваш String получает отличное от Integer.