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

时间:2009-09-18 15:21:25

标签: java generics

这是相关代码的简化版本,一个泛型类使用具有泛型类型参数的另一个类,并且需要将一个泛型类型传递给具有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 });

不起作用,因为您无法创建通用数组。

8 个答案:

答案 0 :(得分:84)

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

这个bug report有更多的信息,但归结为编译器不喜欢泛型类型的数组。

答案 1 :(得分:57)

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

正如您在Joseph Darcy's blog post中所看到的,项目硬币练习为Java 7选择了一些小的增量语言改进,接受Bob Lee's proposal以允许方法端的@SuppressWarnings("varargs")之类的内容进行此操作警告在已知安全的情况下消失。

这已在OpenJDK中使用this commit实现。

这可能对您的项目有用,也可能没有用(很多人不愿意切换到JVM的预发布不稳定版本!)但也许是 - 或者可能是某个人稍后发现此问题(之后) JDK7将会发现它很有用。

答案 2 :(得分:16)

如果您使用的是流畅的类型界面,则可以尝试使用构建器模式。不像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);
        }
    }
}

答案 3 :(得分: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" ) )

我认为这里的问题是编译器需要弄清楚要创建的具体类型的数组。如果该方法不是通用的,则编译器可以使用该方法中的类型信息。如果该方法是通用的,它会尝试根据调用时使用的参数计算出数组类型。如果参数类型是同源的,那么该任务很容易。如果它们不同,编译器会在我看来太聪明并创建一个union-type泛型数组。然后它感到有必要警告你。当类型无法更好地缩小时,更简单的解决方案就是创建Object []。上述解决方案就是这样。

为了更好地理解这一点,请与以下list2方法相比,调用上面的列表方法。

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

答案 4 :(得分:2)

您可以在Java 7之后向方法添加@SafeVarargs,而不必在客户端代码上添加注释。

class Assembler<X, Y> {

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

答案 5 :(得分:1)

这是一个非常容易解决的问题:使用List<T>

应避免引用类型的数组。

在当前版本的Java(1.7)中,您可以使用@SafeVargs标记方法,该方法将从调用方中删除警告。但是要小心,如果没有传统阵列,你仍然会感觉更好。

另请参阅Improved Compiler Warnings and Errors When Using Non-Reifiable Formal Parameters with Varargs Methods技术说明。

答案 6 :(得分: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) { ... }
}

答案 7 :(得分:0)

使用泛型类型的数组时,我被迫传递对泛型类型的引用。有了它,我实际上可以使用java.lang.reflect.Array。

来完成通用代码

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