Как родовая функция реализована в Java?

Согласно моему пониманию следующей родовой функции в 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;
    }
10
задан 6 revs, 5 users 63% 3 February 2010 в 03:15
поделиться

3 ответа

Универсальные шаблоны используют стирание типа. По сути, это означает, что обобщенные типы - это не что иное, как неявное приведение типов, поэтому когда вы делаете:

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 () возвращать все, что вам нравится, а не то, что он должен вернуть на основе параметризованного типа.

7
ответ дан 3 December 2019 в 22:37
поделиться

Кто-нибудь, обладающий более глубокими знаниями в этой теме, может присоединиться к нему позже, а мое скромное объяснение таково:

Как вы говорите, у дженериков есть стирание типов , что означает, что общая информация недоступна на время выполнения. Это , но доступно во время компиляции , и именно так компилятор может определить, что вы смешиваете типы.

Надеюсь, это поможет!

6
ответ дан 3 December 2019 в 22:37
поделиться

В вашем примере компилятор считает, что тип возвращаемого значения является тем же типом, что и параметр, переданный функции, поскольку оба они параметризованы для типа T, который разрешен Целое число . При генерации байт-кода он затем стирает информацию о параметрах типа.

2
ответ дан 3 December 2019 в 22:37
поделиться
Другие вопросы по тегам:

Похожие вопросы: