这是相关代码的简化版本,一个泛型类使用具有泛型类型参数的另一个类,并且需要将一个泛型类型传递给具有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 });
不起作用,因为您无法创建通用数组。
答案 0 :(得分:84)
除了添加@SuppressWarnings("unchecked")
之外,我不这么认为。
这个bug report有更多的信息,但归结为编译器不喜欢泛型类型的数组。
答案 1 :(得分:57)
正如您在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
标记方法,该方法将从调用方中删除警告。但是要小心,如果没有传统阵列,你仍然会感觉更好。
答案 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