【问题标题】:Java generics and varargsJava 泛型和可变参数
【发布时间】: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)”并消除警告,但这也消除了编译时类型检查 --- 如果我想这样做就不太好确保正确使用此功能。有更好的解决方案吗?

【问题讨论】:

    标签: java generics types variadic-functions


    【解决方案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 后的世界中。

    【讨论】:

    【解决方案3】:

    顺便说一句,现在可以使用 Java 7 的新 @SafeVarargs 注释来抑制警告。

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

    【讨论】:

      【解决方案4】:

      我对这个问题的解决方案是

      1. 创建一个类 Nastier
      2. 从 doNastyThingsToClasses 中删除 ...
      3. 使 doNastyThingsToClasses 没有静态方法
      4. 名字要简短,就像这样做
      5. 返回这个
      6. 将重复的参数移动到类属性中

        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 询问的是前两行的警告,而不是错误。
      猜你喜欢
      • 1970-01-01
      • 2011-05-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-18
      • 1970-01-01
      • 1970-01-01
      • 2017-10-06
      相关资源
      最近更新 更多