是否有可能解决“为varargs参数创建T的通用数组”的编译器警告?


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泛型常见问题angelikalanger.com/GenericsFAQ/FAQSections/...
流量

Answers:


88

除了添加外@SuppressWarnings("unchecked"),我不这么认为。

错误报告具有更多信息,但归结为编译器不喜欢泛型类型的数组。


3
忘了提我要避免@SuppressWarnings(“ unchecked”)。该错误报告给了我一点希望!
马特b

3
正如约书亚·布洛赫(Joshua Bloch)在“有效Java”中所说的那样:“不要混合泛型和数组。”
Timmos 2014年

20
然后,隐式地:不要将Varargs与泛型一起使用!对...将varargs映射到Array而不是Collection的决定将永远刺痛Java。戈斯林先生做得很好。
bernstein

57

Tom Hawtin在评论中指出了这一点,但更明确地说:是的,您可以在声明站点(而不是(可能有很多)调用站点)解决此问题:切换到JDK7。

正如您在Joseph Darcy的博客文章中所看到的,为Java 7选择一些小的增量语言改进的项目硬币练习接受了鲍勃·李的提议@SuppressWarnings("varargs"),该提议允许在方法端使这种警告在已知的情况下消失。安全。

这已在OpenJDK的commit中实现

这可能对您的项目有用,也可能没有用(许多人都不愿意切换到JVM的预发行不稳定版本!),但也许是有用的,或者是后来(在JDK7退出后)发现此问题的人)会发现它很有用。


7
所提到的Coin项目功能现在-见@SafeVarargs在Java 7中
乔治·霍金斯

鲍勃提议中的备选方案E具有吸引力。
Christopher Perry

Java 8似乎使用@SafeVarargs而不是@SuppressWarnings(“ varargs”)
Paul Wintz

17

如果您使用的是流畅类型的界面,则可以尝试使用构建器模式。不像varargs一样简洁,但是它是类型安全的。

使用生成器时,静态的通用类型方法可以消除某些样板,同时保持类型安全。

建造者

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
组合的力量。我比varargs更喜欢它,它更具表现力。
Christopher Perry

1
@ChristopherPerry好,您也必须考虑您的代码库。底层Collection(在这种情况下为ArrayList)被强加到调用者上,而他们可能知道a LinkedList更合适,或者数组本身是不可变的(例如OP问题中的varargs)。在非专业的用例中,这可能是适当的,但只是指出,在某种程度上,这也是一种限制,具体取决于围绕此的代码和您的需求。
searchengine27

5

在vararg方法调用中将参数显式转换为Object会使编译器满意,而无需诉诸@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方法相比,对上面的list方法进行调用。

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

例如,它也可以工作:Iterator <?> it = Arrays.asList((Object)t).iterator; if(if,hasNext()){class = it.next()。getClass(); 例如,从未知类型的数组中获取对象的类。
ggb667

2

从Java 7开始,您可以将@SafeVarargs添加到方法中,而不必在客户端代码上进行注释。

class Assembler<X, Y> {

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

1

您可以重载方法。这不能解决您的问题,但是可以最大限度地减少警告的数量(是的,这是hack!)

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
w 这正是varargs应该防止的黑客行为。
阿曼达·S

1
这可能是一种有效的方法,例如,看看Guava的ImmutableSet.of
乔纳森

1

解决一个非常简单的问题:使用List<T>

应该避免引用类型的数组。

在Java(1.7)的当前版本中,您可以标记方法,使用@SafeVargs该方法将从调用者中删除警告。不过要小心,如果没有传统阵列,您的生活会更好。

另请参阅在Varargs方法中使用不可修改的形式参数时改进的编译器警告和错误技术说明。


6
这是使用varargs参数不可避免的,不是吗?
马特b 2009年

4
对于JDK7,有一个建议允许警告抑制在varargs方法声明而不是其用法上进行。
汤姆·哈特芬

11
这完全忽略了作者问题的一个重要方面-varargs参数确实创建了一个数组,并生成了此警告。
Daniel Yankowsky 2010年

2
我同意@Tom Hawtin-大头贴。有关详细信息,请参见Bloch << Efcive Java >> Item 25:首选列表而不是数组。
斯坦·库里林

2
我通常同意布洛赫(Bloch)的观点,但是varargs是该规则的明显例外...
Joeri Hendrickx 2010年

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.