Подтвердить что ты не робот

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)" и избавиться от предупреждений, но это также удаляет проверку типа времени компиляции - не так хорошо, если я хочу обеспечить правильное использование этой функции. Любое лучшее решение?

4b9b3361

Ответ 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);

    }

}

Единственный недостаток - это длинный код для заполнения коллекции, используемой для переноса аргументов функции.