Почему этот общий код в java компилируется?

public static <T> T foo(T x, T x2) {
    return (T) (x + "   " + x2);
}

public static void main(String args[]) {
    System.out.println(foo(33, "232"));
}

Я знаю, что T получает тип, который передается в параметре. Но здесь есть два типа. Какой из них T?

и почему компилятор не заставляет меня иметь параметры того же типа, когда я вызываю foo?

Ответ 1

Это законно, потому что Integer и String имеют общий базовый тип. T может быть Object, поэтому foo(T, T) будет принимать любые два объекта. 32 может быть автобокс с Integer, что является видом Object. "232" является String, что является видом Object.

(Как отмечалось в комментариях, для Integer и String существует более близкий общий базовый тип Serializable, но принцип тот же, какой бы базовый тип не разрешал компилятор T to.)

Ответ 2

public static <T> T foo(T x, T x2) {
    return (T) (x + "   " + x2);
}

Следует отметить, что во время компиляции он может выйти из строя во время выполнения:

int f = foo(1, 2);

выйдет из строя Ideone demo, потому что метод возвращает String, а не Integer.

Он компилируется, однако, поскольку стирание стилей означает, что T переписывается на верхнюю границу, а именно Object. Итак:

public static Object foo(Object x, Object x2) {
  return (Object) (x + "   " + x2);
}

Это хорошо, потому что результатом является String, который является подтипом Object.

Я предполагаю, что это всего лишь случай, когда модели дженериков ожидают вас. Вот почему я бы избегал смешивать дженерики и явное литье.