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&lt;C&gt; 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&lt;? extends A&gt;...必须扩展第一个参数所在的类(例如,参数一个是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 泛型和可变参数的主要内容,如果未能解决你的问题,请参考以下文章

JAVA中,关于可变参数和泛型的问题。

Java泛型类型通配符 可变参数

Java实训笔记——-抽象类-接口-泛型-集合

java泛型中可变参数的是使用

Kotlin泛型 ② ( 可变参数 vararg 关键字与泛型结合使用 | 使用 [] 运算符获取指定可变参数对象 )

如何创建可变参数泛型 lambda?