Дженерики Java и varargs - PullRequest
       34

Дженерики Java и varargs

31 голосов
/ 23 июня 2010

Я хотел бы реализовать функцию с обобщениями и переменными.

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

Ответы [ 6 ]

38 голосов
/ 23 июня 2010

Как почти всегда, в FAQ по дженерикам Java от Angelika Langer объясняется очень подробно . (Перейдите к разделу «Почему компилятор иногда выдает непроверенное предупреждение, когда я вызываю метод« varargs »?» - идентификатор не работает должным образом.)

По сути, вы теряете информацию хуже, чем обычно. Еще одна небольшая болевая точка в дженериках Java: (

13 голосов
/ 23 июня 2010

ответ Джона Скита (конечно) верен; Я немного подробнее остановлюсь на этом, указав, что вы МОЖЕТЕ избавиться от этого предупреждения с большим «если». Вы можете избежать этого предупреждения, ЕСЛИ вы готовы посвятить себя созданию проекта с использованием Java 7.

Боб Ли написал предложение разрешить подавление этого предупреждения на сайте объявления методов, а не на сайте использования, как часть Project Coin .

Это предложение было принято для JDK7 (хотя синтаксис немного изменился, до @SuppressWarnings("varargs")); вы можете, если вам интересно, посмотреть коммит, который добавил эту поддержку в JDK .

Не обязательно полезно для вас, но я подумал, что сделаю это отдельным ответом, чтобы он продолжал жить для будущих читателей, которым, возможно, повезет жить в мире после Java-7.

9 голосов
/ 28 июля 2011

Кроме того, теперь предупреждение можно подавить с помощью новой аннотации @SafeVarargs в Java 7.

@SafeVarargs
public static <A> void func( Class<A> parent, Class<? extends A>... classes ) {
    // Do func...
}
5 голосов
/ 25 сентября 2011

Мое решение этой проблемы было

  1. создать класс Nastier
  2. удалить ... из doNastyThingsToClasses
  3. make doNastyThingsToClasses none статический метод
  4. сделать имя коротким, как do
  5. вернуть это
  6. переместить повторяющиеся аргументы в свойства класса

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

Я считаю, чтокод выглядит чистым, и я счастлив ....:)

1 голос
/ 23 июня 2010

ОК, так что в конце концов я выбрасываю вараггов:

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

    }

}

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

0 голосов
/ 23 июня 2010

Второй аргумент Class<? extends A> ..., который должен расширять класс, которым является первый аргумент (например, аргумент один является Question, поэтому второй аргумент должен быть чем-то, что расширяет Question.

Разбивка:
NastyThingsToClasses(Object.class, Question.class, SomeQuestion.class); // OK
Все расширяется Object, поэтому второй аргумент верен.

NastyThingsToClasses(Question.class, SomeQuestion.class); // OK
SomeQuestion расширяет Question, так что это честная игра.

NastyThingsToClasses(Question.class, Object.class, SomeQuestion.class);
Object не расширяет Question, следовательно, ошибка.


надеюсь, что все прояснилось.

-Brett

...