Чи можна вирішити попередження компілятора "Створений загальний масив T для параметра varargs"?


153

Це спрощена версія розглянутого коду, один загальний клас використовує інший клас із загальними параметрами типу і йому потрібно передати один із загальних типів методу з параметрами varargs:

class Assembler<X, Y> {
    void assemble(X container, Y... args) { ... }
}

class Component<T> {
    void useAssembler(T something) {

        Assembler<String, T> assembler = new Assembler<String, T>();

        //generates warning:
        // Type safety : A generic array of T is
        // created for a varargs parameter
        assembler.assemble("hello", something);
    }

}

Чи є правильний спосіб передавати загальний параметр методу varargs, не стикаючись з цим попередженням?

Звичайно щось на кшталт

assembler.assemble("hello", new T[] { something });

не працює, оскільки ви не можете створити загальні масиви.


3
Дивна. Схоже, компілятор повинен бути в змозі забезпечити повну безпеку тут.
erickson

3
Пов’язаний запис у Java Generics Анжеліки Лангер: поширені запитання: angelikalanger.com/GenericsFAQ/FAQSections/…
Потік

Відповіді:


88

Крім додавання @SuppressWarnings("unchecked"), я не думаю.

Цей звіт про помилку містить більше інформації, але він зводиться до компілятора, який не подобається масивам загальних типів.


3
Забув згадати, що хотів уникнути @SuppressWarnings ("невірно"). Цей звіт про помилку дає мені мало надії!
matt b

3
Як Джошуа Блох вказує на «Ефективна Java»: «Не змішуйте генерики та масиви».
Тиммос

20
то, неявно: не користуйтеся Varargs з Generics! Правильно ... рішення про розміщення вараг до Array, а не Collection збереже жалобну Java назавжди. Чудово виконаний містер Гослінг.
Бернштейн

57

Том Хоутін вказав на це у коментарі, але щоб бути більш чітким: так, ви можете вирішити це на сайті декларації (а не на (потенційно багатьох) викликових сайтах): перейдіть на JDK7.

Як ви можете бачити в публікації блогу Джозефа Дарсі , проект "Монета" для вибору деяких невеликих удосконалень мови для Java 7 прийняв пропозицію Боба Лі, щоб дозволити щось на зразок @SuppressWarnings("varargs")методу усунути це попередження в ситуаціях, коли, як відомо, було сейф.

Це було реалізовано в OpenJDK з цим зобов’язанням .

Це може бути або не корисним для вашого проекту (багато людей не будуть раді перейти на нестабільну версію JVM до попереднього випуску!), Але, можливо, це - або, можливо, хтось знайде це питання пізніше (після того, як JDK7 вийде ) знайде це корисним.


7
Згадана функція Project Coin тепер доступна - див. @SafeVarargs на Java 7.
Джордж Хокінс

Альтернатива Е в пропозиції Боба є привабливою.
Крістофер Перрі

Як видається, Java 8 використовує @SafeVarargs замість @SuppressWarnings ("varargs")
Пол Вінц

17

Якщо ви користуєтеся інтерфейсом плавного типу, ви можете спробувати модель конструктора. Не настільки стислий, як варагги, але це безпечно.

Статичний метод загального типу може усунути частину котлоагрегату при використанні будівельника, зберігаючи при цьому безпеку типу.

Будівельник

public class ArgBuilder<T> implements Iterable<T> {

    private final List<T> args = new ArrayList<T>();

    public ArgBuilder<T> and(T arg) {
        args.add(arg);
        return this;
    }

    @Override
    public Iterator<T> iterator() {
        return args.iterator();
    }

    public static <T> ArgBuilder<T> with(T firstArgument) {
        return new ArgBuilder<T>().and(firstArgument);
    }
}

Використовуючи його

import static com.example.ArgBuilder.*;

public class VarargsTest {

    public static void main(String[] args) {
        doSomething(new ArgBuilder<String>().and("foo").and("bar").and("baz"));
        // or
        doSomething(with("foo").and("bar").and("baz"));
    }

    static void doSomething(Iterable<String> args) {
        for (String arg : args) {
            System.out.println(arg);
        }
    }
}

1
Сила композиції. Мені це подобається набагато більше, ніж вараги, це виразніше.
Крістофер Перрі

1
@ChristopherPerry, ви також повинні врахувати свою кодову базу. Основний Collection(у даному випадку ан ArrayList) примусовий абонент, тоді як вони можуть знати, що а LinkedListє більш підходящим, або сам незмінний масив (наприклад, вараги з питання про ОП). У випадку неспеціалізованого використання це може бути доречним, але лише зазначивши, що це також обмеження, певним чином, залежно від коду, що оточує це та ваших потреб.
searchchengine27

5

Явне введення параметрів до виклику методу Object in vararg зробить компілятора щасливим, не вдаючись до @SuppressWarnings.

public static <T> List<T> list( final T... items )
{
    return Arrays.asList( items );
}

// This will produce a warning.
list( "1", 2, new BigDecimal( "3.5" ) )

// This will not produce a warning.
list( (Object) "1", (Object) 2, (Object) new BigDecimal( "3.5" ) )

// This will not produce a warning either. Casting just the first parameter to 
// Object appears to be sufficient.
list( (Object) "1", 2, new BigDecimal( "3.5" ) )

Я вважаю, що тут проблема полягає в тому, що компілятору потрібно розібратися, який конкретний тип масиву потрібно створити. Якщо метод не є загальним, компілятор може використовувати інформацію про тип із методу. Якщо метод є загальним, він намагається з'ясувати тип масиву на основі параметрів, що використовуються при виклику. Якщо типи параметрів є однорідними, це завдання просте. Якщо вони різняться, компілятор намагається бути занадто розумним на мій погляд і створює загальний масив об'єднаного типу. Тоді він змушений попередити вас про це. Більш простим рішенням було б створити Object [], коли тип не можна краще звузити. Наведене вище рішення змушує саме це.

Щоб зрозуміти це краще, пограйте з викликами до вищевказаного методу списку порівняно із наведеним нижче методом list2.

public static List<Object> list2( final Object... items )
{
    return Arrays.asList( items );
}

Це працює, наприклад: Iterator <?> It = Arrays.asList ((Object) t) .iterator; if (якщо, hasNext ()) {class = it.next (). getClass (); } наприклад, щоб отримати клас об'єкта з масиву невідомого типу.
ggb667

2

Ви можете додати @SafeVarargs до методу, починаючи з Java 7, і вам не доведеться коментувати код клієнта.

class Assembler<X, Y> {

    @SafeVarargs
    final void assemble(X container, Y... args) {
        //has to be final...
    }
}

1

Ви можете перевантажити методи. Це не вирішує вашу проблему, але мінімізує кількість попереджень (і так, це злом!)

class Assembler<X, Y> {
  void assemble(X container, Y a1) { ... }
  void assemble(X container, Y a1, Y a2) { ... }
  void assemble(X container, Y a1, Y a2, Y a3) { ... }
  void assemble(X container, Y a1, Y a2, Y a3, Y a4) { ... }
  void assemble(X container, Y... args) { ... }
}

23
Ew Це саме той вид зламу, який, як передбачається, запобігають вараги.
Amanda S

1
Це може бути правильним підходом, наприклад, подивіться на гуави в ImmutableSet.of .
Джонатан

1

Вирішити це дуже просто: Використовуйте List<T>!

Слід уникати масивів еталонного типу.

У поточній версії Java (1.7) ви можете позначити метод, за допомогою @SafeVargsякого буде видалено попередження від абонента. Але будьте обережні з цим, і вам все одно краще без застарілих масивів.

Див. Також Поліпшені попередження та помилки компілятора при використанні формальних параметрів, які не підлягають повторенню, із технікою примітки методів Varargs .


6
це неминуче за допомогою параметра varargs, чи не так?
мат b

4
Існує пропозиція JDK7 дозволити придушення попередження переходити до декларації методу varargs, а не до його використання.
Том Хотін - тайклін

11
Це повністю ігнорує важливий аспект питання автора - параметри varargs DO створюють масив, і це генерує це попередження.
Даніель Янковський

2
Я згоден з @Tom Hawtin - tackline. Докладніше див. Bloch << Effecive Java >> Пункт 25: Переважні списки до масивів.
Стен Курілін

2
Я, як правило, погоджуюся з Блохом з цього приводу, але варагги - це явний виняток із правила ...
Joeri Hendrickx

0

Під час роботи з масивами загального типу я змушений передати посилання на загальний тип. З цим я фактично можу зробити загальний код, використовуючи java.lang.reflect.Array.

http://java.sun.com/javase/6/docs/api/java/lang/reflect/Array.html


Я не працюю з масивами загального типу, хоча, безпосередньо, просто вараграми загального типу.
мат b
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.