2014-10-02 3 views
5

Если я, например, есть метод, который использует с переменным числом аргументов для типов класса, который расширяет суперкласс как этого:списков параметров типа класса в Java

public static <E extends Example> void test(Class<E>... es){} 

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

//this does not work 
test(E1.class,E2.class); 
//this does work 
test(new Class[]{E1.class,E2.class}); 
public class E1 extends Example {} 
public class E2 extends Example {} 

Почему это?

ответ

6

Эта строка не компилируется:

test(E1.class,E2.class); 

Существует только один параметр типа E и Java должны соответствовать выведенные типы аргументов точно. Он не может вывести Example, потому что объекты Class<E1> и Class<E2>, а не Class<Example>. Инвариантность Java-дженериков предотвращает это.

Вы можете обойти эту проблему путем введения верхней грани подстановочного по родовому параметру типа test «s:

public static <E extends Example> void test(Class<? extends E>... es) 

Это позволяет Java сделать вывод Example для E, удовлетворяя верхнюю границу подстановочного с E1 и E2.

Вторая строка создает необработанный массив Class es, минуя дженерики и генерируя предупреждение «unchecked call».

new Class[]{E1.class,E2.class} 

Если вы должны были попытаться предоставить аргумент типа для Class здесь, вы получите ошибку компиляции с любым полупутем разумного параметром типа:

// Needs Class<Example> but found Class<E1> and Class<E2> 
test(new Class<Example>[]{E1.class,E2.class}); 

// Needs Class<E1> but found Class<E2> 
test(new Class<E1>[]{E1.class,E2.class}); 

// Needs Class<E2> but found Class<E1> 
test(new Class<E2>[]{E1.class,E2.class}); 

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

// Generic array creation 
test(new Class<? extends Example>[]{E1.class,E2.class}); 
+0

Большое спасибо. Это никогда не пагубно относилось к моей программе, но я определенно почесывал ее на некоторое время. Я очень благодарен за разъяснение. – Squirvin

2

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

Хотя это не работает:

test(E1.class, E2.class); 

Это делает:

test(E1.class, E1.class); 

Причина вы можете сделать это с массивом из-за типа стирания. Компилятор не видит, что классы внутри массива разные.

Если вы изменили свой метод, чтобы принять любой класс, который расширяет Example, он будет работать.

public static void test(Class<? extends Example>...classes) 
+1

Я думаю, вы объяснили это довольно хорошо. Но чтобы быть равным примеру OP, ваша подпись должна быть «public static void test (Class ... classes)». Тем не менее, он (в любом случае) даст предупреждение «-Xlint: unchecked». – 5gon12eder

+0

@ 5gon12eder: Подпись, которую вы дали, и та, что в этом ответе, в точности эквивалентны, то есть они принимают тот же самый класс аргументов. Более простой (тот, который указан в этом ответе) всегда должен быть предпочтительным, поскольку параметр типа не нужен. – newacct

+0

@newacct Они принимают тот же класс аргументов, но если вы хотите обратиться к родовому типу, вам понадобится более подробный синтаксис. Поскольку в OP 'test' возвращает' void' и имеет пустое тело, вы можете утверждать, что мы вообще не хотим ссылаться на тип, но, говоря в целом, подробный синтаксис также более эффективен. – 5gon12eder

Смежные вопросы