Java-дженерики и varargs
Я хотел бы реализовать функцию как с генераторами, так и с varargs.
public class Question {
public static <A> void doNastyThingsToClasses(Class<A> parent, Class<? extends A>... classes) {
/*** something here ***/
}
public static class NotQuestion {
}
public static class SomeQuestion extends Question {
}
public static void main(String[] args) {
doNastyThingsToClasses(Object.class, Question.class, SomeQuestion.class); // OK
doNastyThingsToClasses(Question.class, SomeQuestion.class); // OK
doNastyThingsToClasses(Question.class, Object.class, SomeQuestion.class); // compilation failure
}
}
Цель состоит в том, чтобы утверждать, что все параметры, переданные этой функции, являются объектами класса, расширяющими класс, указанный в качестве первого параметра. Таким образом, две первые строки основного метода будут скомпилированы, а третий - ошибка.
Мой вопрос: Почему я получаю "Тип безопасности: генерируется общий массив класса для параметра varargs" для первых двух строк?
Я что-то пропустил?
Дополнительный вопрос:, как его переделать, чтобы это предупреждение не отображалось в каждой строке, вызывающей функцию doNastyThingsToClasses? Я могу изменить его на "doNastyThingsToClasses (класс <A> parent, Class <? > ... classes)" и избавиться от предупреждений, но это также удаляет проверку типа времени компиляции - не так хорошо, если я хочу обеспечить правильное использование этой функции. Любое лучшее решение?
Ответы
Ответ 1
Как всегда, часто задаваемые вопросы о генераторах Angelika Langer Java подробно объясняет это. (Прокрутите до "Почему компилятор иногда выдает предупреждение без предупреждения при вызове метода" varargs "?" - идентификатор не работает хорошо.)
В принципе, вы теряете информацию хуже, чем обычно. Еще одна небольшая болевая точка в Java-дженериках: (
Ответ 2
Ответ Джона Скита (конечно) правильный; Я немного расширю его, указав, что вы можете избавиться от этого предупреждения, с большим "если". Вы можете избежать этого предупреждения, если вы согласны с тем, что ваш проект построен с использованием Java 7.
Боб Ли написал предложение, чтобы это предупреждение было подавлено на сайте объявления метода, а не на сайте использования, как часть Монета проекта.
Это предложение было принято для JDK7 (хотя синтаксис слегка изменился, до @SuppressWarnings("varargs")
); вы можете, если вам интересно, посмотреть фиксацию, которая добавила эту поддержку в JDK.
Не обязательно полезно для вас, но я думал, что сделаю это отдельным ответом, чтобы он жил для будущих читателей, которым, возможно, повезло жить в мире после Java-7.
Ответ 3
В стороне, предупреждение теперь можно подавить с помощью новой аннотации @SafeVarargs Java 7.
@SafeVarargs
public static <A> void func( Class<A> parent, Class<? extends A>... classes ) {
// Do func...
}
Ответ 4
Мое решение этой проблемы состояло в
- создать класс Nastier
- удалить... из doNastyThingsToClasses
- make doNastyThingsToClasses none static method
- сделать имя коротким, например do
- вернуть это
-
переместить повторяющиеся аргументы в свойства класса
class Nastier {
private final Class<A> parent;
public Nastier(Class<A> parent) {
this.parent = parent;
}
public <A, C extends A> Nastier do(Class<? extends A> clazz) {
System.out.println(clazz);
return this;
}
}
public static void main(String[] args) {
Nastier nastier = new Nastier(Object.class);
nastier.do(Question.class).do(SomeQuestion.class).do(NotQuestion.class);
}
Я считаю, что код выглядит чистым, и я счастлив....:)
Ответ 5
Второй аргумент Class<? extends A>
..., который должен расширять класс, который имеет первый аргумент (например, аргумент one является Question
, поэтому вторым аргументом является то, что расширяет Question
.
The Breakdown:
NastyThingsToClasses(Object.class, Question.class, SomeQuestion.class); // OK
Все расширяет Object
, поэтому второй аргумент правильный.
NastyThingsToClasses(Question.class, SomeQuestion.class); // OK
SomeQuestion
extends Question
, так что это справедливая игра.
NastyThingsToClasses(Question.class, Object.class, SomeQuestion.class);
Object
не распространяется Question
, следовательно, ошибка.
надеюсь, что это прояснилось.
-Brett
Ответ 6
ОК, так что, наконец, я в конечном итоге выбрасываю varargs:
public class Question {
public static <A, C extends A> void doNastyThingsToClasses(Class<A> parent, List<Class<? extends A>> classes) {
/******/
for(Class<? extends A> clazz : classes) {
System.out.println(clazz);
}
}
public static class NotQuestion {
}
public static class SomeQuestion extends Question {
}
public static void main(String[] args) {
ArrayList<Class<? extends Object>> classes = new ArrayList<Class<? extends Object>>();
classes.add(Question.class);
classes.add(SomeQuestion.class);
classes.add(NotQuestion.class);
doNastyThingsToClasses(Object.class, classes);
ArrayList<Class<? extends Question>> clazzes = new ArrayList<Class<? extends Question>>();
clazzes.add(Question.class);
clazzes.add(SomeQuestion.class);
clazzes.add(NotQuestion.class); // yes, this will _not_ compile
doNastyThingsToClasses(Question.class, clazzes);
}
}
Единственный недостаток - это длинный код для заполнения коллекции, используемой для переноса аргументов функции.