Java泛型和varargs

时间:2010-06-22 20:04:37

标签: java generics types variadic-functions

我想用泛型和varargs实现一个函数。

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
    }
}

这里的目的是断言传递给该函数的所有参数都是Class对象,扩展了作为第一个参数给出的Class。因此,main方法的第一行将编译,第三行将生成错误。

我的问题是:为什么我得到“类型安全:前两行为varargs参数创建了一个Class的通用数组”消息?

我在这里错过了什么吗?

其他问题: 如何重新设计它以防止在每行调用“doNastyThingsToClasses”函数时显示此警告?我可以将其更改为“doNastyThingsToClasses(Class&lt; A&gt; parent,Class&lt;?&gt; ... classes)”并删除警告但这也会删除编译时类型检查 - 如果我想要的话就不太好了确保正确使用此功能。有更好的解决方案吗?

6 个答案:

答案 0 :(得分:38)

几乎总是如此,Angelika Langer的Java泛型常见问题explains it in great detail。 (滚动到“当我调用”varargs“方法时,为什么编译器有时会发出未经检查的警告?” - ID不能正常工作。)

基本上,您最终会以比正常情况更糟的方式丢失信息。 Java泛型中的另一个小痛点:(

答案 1 :(得分:13)

Jon Skeet的答案(当然)是正确的;我将通过指出你可以摆脱这个警告,用一个很大的'if'来扩展它。如果您愿意承诺使用Java 7构建项目,则可以避免此警告。

Bob Lee写了a proposal,以便在方法声明网站而不是使用网站上禁止此警告,作为Project Coin的一部分。

此提议已被JDK7接受(虽然语法略有改动,但为@SuppressWarnings("varargs"));你可以,如果你好奇,可以看看the commit that added this support to the JDK

对你来说不一定有用,但我想我会把它作为一个单独的答案,以便它继续为未来的读者而生,他们可能很幸运地生活在后Java-7世界。

答案 2 :(得分:9)

另外,现在可以使用Java 7的新@SafeVarargs注释来抑制警告。

@SafeVarargs
public static <A> void func( Class<A> parent, Class<? extends A>... classes ) {
    // Do func...
}

答案 3 :(得分:5)

我解决这个问题的方法是

  1. 创建一个Nastier类
  2. 从doNastyThingsToClasses中删除...
  3. make doNastyThingsToClasses none static method
  4. 简短地命名,就像做
  5. 返回此
  6. 将重复的args移动到类属性

    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);
    }
    
  7. 我相信代码看起来很干净,我很高兴.... :)

答案 4 :(得分:1)

好的,最后我最终把varargs扔掉了:

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

    }

}

唯一的缺陷是用于填充用于传递函数参数的集合的长代码。

答案 5 :(得分:0)

第二个参数Class<? extends A> ...必须扩展第一个参数所在的类(例如,参数1是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错误。


希望这能解决问题。

-Brett