Как универсальная функция реализована в Java? - PullRequest
10 голосов
/ 03 февраля 2010

Согласно моему пониманию следующая обобщенная функция в 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.Как это понимает компилятор?

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;
    }

Ответы [ 3 ]

7 голосов
/ 03 февраля 2010

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

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, тип возвращаемого значения Integer.Когда вы звоните:

Integer i = f(new Integer(4));

, компилятор просто следует вашим инструкциям.Функция принимает и возвращает Object в скомпилированном виде, но просто делает это:

Integer i = (Integer)f(new Integer(4));

неявно.

Как и в примере List, описанном выше, у вас ничего не останавливаетсяf() возвращает все, что вам нравится, а не то, что он должен возвращать, основываясь на параметризованном типе.

6 голосов
/ 03 февраля 2010

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

То, что вы говорите правильно, генерики имеют тип стирания , что означает, что универсальная информация недоступна во время выполнения. Однако доступно в время компиляции , и именно так компилятор может определить, что вы смешиваете типы.

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

2 голосов
/ 03 февраля 2010

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

...