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

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

由于无法创建泛型数组,因此无法工作。

60505 次浏览

在 Java6中,除了添加 @SuppressWarnings("unchecked")之外,我不这样认为。

这个 漏洞报告有更多的信息,但归根结底是编译器不喜欢泛型类型的数组。

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

应避免引用类型的数组。

在 Java 的当前版本(1.7)中,您可以使用 @SafeVargs标记方法,这将删除调用者的警告。不过要小心,没有遗留数组还是比较好的。

List.of()提供了一种相对简洁的方法来编写(不可修改的) List,直到 Java 获得适当的文字表示。

另见 用 Varargs 方法改进形式参数不可验证时的编译器警告和错误技术说明。

当使用泛型类型的数组时,必须传递对泛型类型的引用。有了这些,我就可以使用 java.lang.response 来实现通用代码了。数组。

Http://java.sun.com/javase/6/docs/api/java/lang/reflect/array.html

可以重载这些方法。 这并不能解决您的问题,但是它可以最小化警告的数量(是的,这是一种黑客行为!)

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) { ... }
}

在 Java7中,用 @ SafeVarargs注释方法声明

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

如果您想要一个流畅类型的接口,您可以尝试构建器模式。虽然不像 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);
}
}
}

从 Java7开始,您就可以将 @ SafeVarargs添加到方法中,并且不需要在客户机代码上进行注释。

class Assembler<X, Y> {


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