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

mat*_*t b 150 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);
    }
Run Code Online (Sandbox Code Playgroud)

}

有没有正确的方法将泛型参数传递给varargs方法而不会遇到此警告?

当然有点像

assembler.assemble("hello", new T[] { something });
Run Code Online (Sandbox Code Playgroud)

由于无法创建通用数组,因此无效.

Kev*_*vin 87

除了补充@SuppressWarnings("unchecked"),我不这么认为.

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

  • 然后,含蓄地:不要使用泛型用户Varargs!对......决定将varargs映射到Array而不是Collection将永远刺激java.很好地完成了Gosling先生. (19认同)
  • 忘记提及我想避免@SuppressWarnings("未选中").那个bug报告给了我很少的希望! (3认同)
  • 正如Joshua Bloch在Effective Java中所说:"不要混淆泛型和数组." (3认同)

Cow*_*wan 57

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

正如您在Joseph Darcy的博客文章中所看到的那样,项目硬币练习为Java 7选择了一些小的增量语言改进,接受了Bob Lee的建议,允许类似@SuppressWarnings("varargs")方法方面的事情在已知的情况下使这个警告消失安全.

这已在OpenJDK中通过此提交实现.

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

  • 上面提到的Project Coin功能现已推出 - 请参阅Java 7中的[@SafeVarargs](http://docs.oracle.com/javase/7/docs/api/java/lang/SafeVarargs.html). (7认同)

npg*_*all 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

使用它

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Kon*_*hik 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" ) )
Run Code Online (Sandbox Code Playgroud)

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

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

public static List<Object> list2( final Object... items )
{
    return Arrays.asList( items );
}
Run Code Online (Sandbox Code Playgroud)