Java 泛型和可变参数

     2023-03-31     159

关键词:

【中文标题】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 询问的是前两行的警告,而不是错误。

泛型和泛型集合

 泛型:通过参数化类型来实现在同一份代码上操作多种数据类型。利用“参数化类型”将类型抽象化,从而实现灵活的复用。usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Web;usingSystem.Web.UI;usingSystem.Web.UI.WebCon... 查看详情

0513泛型和学生信息(代码片段)

泛型和项目1.泛型1.1泛型概述 润物细无声!!! 泛型其实也是一种归纳总结思想的提升,对于数据处理的范围,参数的类型,方法操作的数据...进行了二次剥离!!! 代码中使用泛型之后,可以极大的提高对于代码的复用性,... 查看详情

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

1.1方法的可变参数从JDK1.5之后,定义方法时参数的个数可以变化语法:最后一个数据类型后增加3个点注意:1.可变参数只能处于参数列表的最后;2.一个方法中最多只能包含一个可变参数;3.可变参数的本质就是一个数组,因此... 查看详情

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

Arrays工具类中有个静态的方法:1、publicstatic<T>ListL<T>asList(T...a):返回指定数组支持的固定大小的列表2、返回的集合不能做曾删改操作,可以做修改操作List接口中有个静态方法1、publicstatic<E>List<E>of(E...element):返... 查看详情

Java 泛型和静态工厂方法——语法

】Java泛型和静态工厂方法——语法【英文标题】:JavaGenericsandStaticFactoryMethods--Syntax【发布时间】:2014-01-1203:55:30【问题描述】:这是我得到的:publicclassNode<T>//instancevariablesprivateNode<T>next;privateTdata;//constructwithdataprivateNo... 查看详情

0513泛型和学生信息(代码片段)

泛型和项目1.泛型1.1泛型概述 润物细无声!!! 泛型其实也是一种归纳总结思想的提升,对于数据处理的范围,参数的类型,方法操作的数据...进行了二次剥离!!! 代码中使用泛型之后,可以极大的提高对于代码的复用性,... 查看详情

c#中的泛型和泛型集合

泛型泛型引入了一个概念:类型参数。通过使用类型参数(T)减少了运行时强制转换或装箱操作的风险,通过泛型可以最大限度的重用代码,保护类型的安全及提高性能,他的最常见应用就是创建集合类,可以约束集合类中的元... 查看详情

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

文章目录一、可变参数vararg关键字与泛型结合使用二、使用[]运算符获取指定可变参数对象一、可变参数vararg关键字与泛型结合使用如果泛型类型T的参数是vararg可变参数,则在接收可变参数时,需要使用Array<outT>类型的变量进... 查看详情

java泛型

1、Java泛型其实Java的泛型就是创建一个用类型作为参数的类。就象我们写类的方法一样,方法是这样的method(Stringstr1,Stringstr2),方法中参数str1、str2的值是可变的。而泛型也是一样的,这样写classJava_Generics<K,V>,这里边的K和V就... 查看详情

为什么要使用泛型和迭代器(代码片段)

为什么要使用泛型和迭代器+面试题泛型1)为什么要用泛型?在泛型没有诞生之前,我们经常会遇到这样的问题,如以下代码所示:ArrayListarrayList=newArrayList();arrayList.add("Java");arrayList.add(24);for(inti=0;i<arrayList.size();i++)String... 查看详情

java泛型和类型安全的容器

示例:1publicclassApple{2privatestaticlongcounter;3privatefinallongid=counter++;45publiclongid(){6returnid;7}8}1publicclassOrange{23}1publicclassApplesAndOrangesWithoutGenerics{2@SuppressWarnings({"rawty 查看详情

泛型和object的区别?(代码片段)

泛型声明public<T>TdoSomeThing(Tt)returnt;Object声明publicObjectdoSomeThing(Objectobj)returnobj;泛型引用Stringresult=doSomeThing("参数是String");Object引用Stringresult=(String)doSomeThing("参数是String");使用泛型,有2个好处:1.不需要做强制类型转换2.编译... 查看详情

java泛型和内部类(代码片段)

文章目录一、泛型的概述1.概念2.泛型的定义3.泛型的使用4.泛型的意义5.泛型是如何编译的?二、泛型的进一步使用1.泛型类的定义-类型边界Number类2.泛型方法3.泛型中的父子类型4.通配符?通配符上界通配符下界5.泛型的... 查看详情

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

】如何创建可变参数泛型lambda?【英文标题】:Howtocreateavariadicgenericlambda?【发布时间】:2014-11-1105:07:42【问题描述】:从C++14开始,我们可以使用泛型lambda:autogeneric_lambda=[](autoparam);这基本上意味着它的调用运算符是基于标记为... 查看详情

Java中的泛型和子类型问题[重复]

】Java中的泛型和子类型问题[重复]【英文标题】:GenericsandSubtypeproblemsinJava[duplicate]【发布时间】:2017-01-1515:15:25【问题描述】:首先,由于java有严格的类型系统,程序在编译时检查类型正确性,并在执行前将类加载到字节码验... 查看详情

typeof 泛型和强制类型 [重复]

】typeof泛型和强制类型[重复]【英文标题】:typeofgenericandcastedtype[duplicate]【发布时间】:2019-07-1613:41:30【问题描述】:假设我们有通用方法:publicvoidGenericMethod<T>(Titem)vartypeOf=typeof(T);vargetType=item.GetType();我们使用以下参数调... 查看详情

java泛型

Java中的泛型(GenericType)基本精神:数据类型参数化集合框架中没使用泛型和使用泛型的比较没使用泛型ListmyIntList=newLinkedList();//1myIntList.add(newInteger(100));//2Integerx=(Integer)myIntList.iterator().next();//3  使用泛型List<Inte 查看详情

java基础语法java的泛型和包装类(代码片段)

...章主要是为了后面学习集合框架所做的知识补充。补充了泛型以及包装类两个知识,但是该章泛型的讲解不够全面,主要是为了集合框架学习做铺垫。文章目录1.预备知识-泛型(Generic)1.1泛型的引入1.2泛型的分类... 查看详情