Вы совсем рядом. Время компиляции проверяет все:
a
имеет тип List
, поэтому вызов
a.add("test");
выходит. b
относится к типу (времени компиляции) ArrayList<Integer>
, поэтому
b.get(0)
тоже проверяет. Обратите внимание, что проверки выполняются только для типов переменных времени компиляции. Когда компилятор видит a.add("test")
, он не знает значение времени выполнения объекта, на который ссылается переменная a
. В общем, на самом деле не может (об этом есть результат в теоретической информатике), хотя анализ типа потока управления может уловить много таких вещей. Такие языки, как TypeScript, могут делать удивительные вещи во время компиляции.
Теперь вы можете предположить, что во время выполнения такие вещи могут быть проверены. Увы, в Java они не могут. Java стирает общие типы. Подробности можно найти в статье о стирании типов Java. TL;DR заключается в том, что List<Integer>
во время компиляции становится необработанным List
во время выполнения. У JVM не было способа «овеществить» дженерики (хотя в других языках они есть!), поэтому, когда дженерики были введены, было принято решение, что Java просто удалит дженерики. Таким образом, во время выполнения в вашем коде нет проблем с типом.
Давайте посмотрим на скомпилированный код:
0: new #2 // class java/util/ArrayList
3: dup
4: invokespecial #3 // Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: astore_2
10: aload_2
11: ldc #4 // String test
13: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
18: pop
19: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
22: aload_1
23: iconst_0
24: invokeinterface #7, 2 // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
29: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
32: return
Здесь сразу видно, что проверки типов во время выполнения нет. Таким образом, полный (но, казалось бы, легкомысленный) ответ на ваш вопрос заключается в том, что Java проверяет типы только во время компиляции на основе типов переменных (известных во время компиляции), но параметры универсального типа стираются и код запускается без них.
person
Ray Toal
schedule
06.10.2018
a
является необработанным, что означает, что в нем хранитсяObject
ссылок. Таким образом, вы можете добавить к нему любой класс Java, поскольку все неявно расширяетObject
. Ошибка возникнет, если вы попытаетесь добавитьString
в списокb
. Это приведет к сбою во время компиляции, поскольку сработают дженерики, чтобы этого не произошло. - person Tim Biegeleisen   schedule 06.10.2018List
, во время выполнения этоList<Object>
, так что есть естьadd(Object)
. Компилятор вставляет приведение типа, и это то, что не удастся. - person Elliott Frisch   schedule 06.10.2018List a = b
, вы отключаете эту проверку во время компиляции. - person Peter Lawrey   schedule 06.10.2018