Java 泛型和可变参数
Posted
技术标签:
【中文标题】Java 泛型和可变参数【英文标题】:Java generics and varargs 【发布时间】:2010-06-22 20:04:36 【问题描述】:我想用泛型和可变参数实现一个函数。
public class Question
public static <A> void doNastyThingsToClasses(Class<A> parent, Class<? extends A>... classes)
/*** something here ***/
public static class NotQuestion
public static class SomeQuestion extends Question
public static void main(String[] args)
doNastyThingsToClasses(Object.class, Question.class, SomeQuestion.class); // OK
doNastyThingsToClasses(Question.class, SomeQuestion.class); // OK
doNastyThingsToClasses(Question.class, Object.class, SomeQuestion.class); // compilation failure
这里的目的是断言传递给这个函数的所有参数都是类对象,扩展了作为第一个参数给出的类。所以 main 方法的前两行会编译,而第三行会产生错误。
我的问题是:为什么我在前两行收到“类型安全:为可变参数参数创建了一个类的通用数组”消息?
我错过了什么吗?
附加问题:如何重新设计它以防止在调用“doNastyThingsToClasses”函数的每一行上显示此警告?我可以将其更改为“doNastyThingsToClasses(Class parent, Class>... classes)”并消除警告,但这也消除了编译时类型检查 --- 如果我想这样做就不太好确保正确使用此功能。有更好的解决方案吗?
【问题讨论】:
【参考方案1】:与往常一样,Angelika Langer 的 Java 泛型常见问题解答 explains it in great detail。 (滚动到“为什么当我调用“可变参数”方法时编译器有时会发出未经检查的警告?” - ID 不能正常工作。)
基本上,您最终会以比正常情况更糟糕的方式丢失信息。 Java泛型的另一个小痛点:(
【讨论】:
我认为这是可变参数的一个痛点——他们不应该使用数组。事实上,错误特征应该被不可变的列表(和其他)文字所取代。 @Tom:是的,那肯定会解决这个特定的问题。当然,数组和泛型放在一起还是很痛苦的:) 至少有一种方法可以解决这个特定的丑陋问题,最终(见我的回答)【参考方案2】:Jon Skeet 的回答(当然)是正确的;我将通过指出你可以用一个大的“如果”来摆脱这个警告来稍微扩展它。如果您愿意承诺使用 Java 7 构建项目,则可以避免此警告。
Bob Lee 作为Project Coin 的一部分,写了a proposal 让这个警告在方法声明站点而不是使用站点被抑制。
JDK7 接受了这个提议(尽管语法略有变化,改为@SuppressWarnings("varargs")
);你可以,如果你好奇的话,看看the commit that added this support to the JDK。
不一定对你有帮助,但我想我会把它作为一个单独的答案,以便将来的读者继续阅读,他们可能有幸生活在 Java-7 后的世界中。
【讨论】:
参见@Scott's answer 关于@SafeVarargs【参考方案3】:顺便说一句,现在可以使用 Java 7 的新 @SafeVarargs 注释来抑制警告。
@SafeVarargs
public static <A> void func( Class<A> parent, Class<? extends A>... classes )
// Do func...
【讨论】:
【参考方案4】:我对这个问题的解决方案是
-
创建一个类 Nastier
从 doNastyThingsToClasses 中删除 ...
使 doNastyThingsToClasses 没有静态方法
名字要简短,就像这样做
返回这个
将重复的参数移动到类属性中
class Nastier
private final Class<A> parent;
public Nastier(Class<A> parent)
this.parent = parent;
public <A, C extends A> Nastier do(Class<? extends A> clazz)
System.out.println(clazz);
return this;
public static void main(String[] args)
Nastier nastier = new Nastier(Object.class);
nastier.do(Question.class).do(SomeQuestion.class).do(NotQuestion.class);
我相信代码看起来很干净,我很高兴.... :)
【讨论】:
我还没有想过方法链...这是一个真的很好的解决方法! 吹毛求疵,但do
是关键字,不能是方法名:-)
应该将do
声明为do(Class<C> clazz)
?【参考方案5】:
好的,所以最后我把可变参数扔掉了:
public class Question
public static <A, C extends A> void doNastyThingsToClasses(Class<A> parent, List<Class<? extends A>> classes)
/******/
for(Class<? extends A> clazz : classes)
System.out.println(clazz);
public static class NotQuestion
public static class SomeQuestion extends Question
public static void main(String[] args)
ArrayList<Class<? extends Object>> classes = new ArrayList<Class<? extends Object>>();
classes.add(Question.class);
classes.add(SomeQuestion.class);
classes.add(NotQuestion.class);
doNastyThingsToClasses(Object.class, classes);
ArrayList<Class<? extends Question>> clazzes = new ArrayList<Class<? extends Question>>();
clazzes.add(Question.class);
clazzes.add(SomeQuestion.class);
clazzes.add(NotQuestion.class); // yes, this will _not_ compile
doNastyThingsToClasses(Question.class, clazzes);
唯一的缺陷是用于填充用于携带函数参数的集合的长代码。
【讨论】:
可以使用 Arrays.asList(...) 轻松完成集合填充【参考方案6】:第二个参数Class<? extends A>
...必须扩展第一个参数所在的类(例如,参数一个是Question
,所以第二个参数是扩展Question
的东西。
故障:NastyThingsToClasses(Object.class, Question.class, SomeQuestion.class); // OK
一切都扩展了Object
,所以第二个参数是正确的。
NastyThingsToClasses(Question.class, SomeQuestion.class); // OK
SomeQuestion
扩展了Question
,这是公平的游戏。
NastyThingsToClasses(Question.class, Object.class, SomeQuestion.class);
Object
没有扩展 Question
因此出错。
希望这能解决问题。
-布雷特
【讨论】:
OP 询问的是前两行的警告,而不是错误。以上是关于Java 泛型和可变参数的主要内容,如果未能解决你的问题,请参考以下文章