Согласно моему пониманию следующей родовой функции в Java:
public static <T> T f(T x) {
Integer[] arr = new Integer[4];
T ret = (T) arr[2];
return ret;
}
компилируется в следующую форму (поскольку это неограниченно):
public static Object f(Object x) {
Integer[] arr = new Integer[4];
Object ret = (Object) arr[2];
return ret;
}
Однако, когда я выполняю следующее утверждение, компилятор в состоянии выяснить возвращаемое значение, чтобы быть Целым типом. Как компилятор понимает это?
Integer i = f(new Integer(4));
Разве функция не должна быть записана как после того, чтобы вышеупомянутый оператор работал?
public static <T extends Integer> T f(T x) {
Integer[] arr = new Integer[4];
T ret = (T) arr[2];
return ret;
}
Универсальные шаблоны используют стирание типа. По сути, это означает, что обобщенные типы - это не что иное, как неявное приведение типов, поэтому когда вы делаете:
List<Integer> ...
, он ничем не отличается от обычного List
и может содержать Integer
или что-то еще. Вы просто говорите Java преобразовать get ()
в Integer
(и другие вещи). Тип просто не сохраняется во время выполнения (в основном).
Массивы разные. Массивы - это так называемые ковариантные . Это означает, что их тип сохраняется во время выполнения. Итак, вы можете сделать:
List<Integer> list1 = new ArrayList<Integer>();
list2 = (List<String>)list1;
list2.add("hello");
, что совершенно законно, будет компилироваться и запускаться. Но:
Integer[] arr1 = new Integer[10];
String[] arr2 = (String[])arr1; // compiler error
Но это тоже становится более тонким.
Integer[] arr1 = new Integer[10];
Object[] arr2 = (Object[])arr1;
arr2[5] = "hello"; // runtime error!
Что касается вашей функции. Когда вы пишете:
public static <T> T f(T x) {
Integer[] arr = new Integer[4];
T ret = (T) arr[2];
return ret;
}
, вы говорите компилятору получить T
, являющийся типом аргумента и типом возвращаемого значения, из аргумента. Поэтому, когда вы передаете Целое число
, возвращаемый тип - Целое число
. Когда вы вызываете:
Integer i = f(new Integer(4));
, компилятор просто следует вашим инструкциям. Функция принимает и возвращает объект
в скомпилированной форме, но делает это:
Integer i = (Integer)f(new Integer(4));
неявно.
Как и в приведенном выше примере List
, ничто не мешает вам f ()
возвращать все, что вам нравится, а не то, что он должен вернуть на основе параметризованного типа.
Кто-нибудь, обладающий более глубокими знаниями в этой теме, может присоединиться к нему позже, а мое скромное объяснение таково:
Как вы говорите, у дженериков есть стирание типов , что означает, что общая информация недоступна на время выполнения. Это , но доступно во время компиляции , и именно так компилятор может определить, что вы смешиваете типы.
Надеюсь, это поможет!
В вашем примере компилятор считает, что тип возвращаемого значения является тем же типом, что и параметр, переданный функции, поскольку оба они параметризованы для типа T, который разрешен Целое число
. При генерации байт-кода он затем стирает информацию о параметрах типа.