我是南城余!阿里云开发者平台专家博士证书获得者!
欢迎关注我的博客!一同成长!
一名从事运维开发的worker,记录分享学习。
专注于AI,运维开发,windows Linux 系统领域的分享!
Java中的泛型,就类似于上述场景中的标签。
泛型
在集合中使用泛型之前可能存在的问题
》类型不安全。因为add()的参数是Object类型,意味着任何类型的对象都可以添加成功
》需要强转操作,繁琐。还可能导致ClassCastException
自定义泛型类、接口、方法
》class Order<T>{}
》public <E>返回值类型 方法名(形参列表){}(<E>在返回值类型前面才能判断其为泛型)
泛型,是程序中出现的不确定的类型
以集合为例,把一个集合中的内容限制在一个特定的数据类型中
使用说明
泛型使用在
集合:ArrayList、HashMap、Iterator
比较器:Comparable、Comparator
》集合框架在声明接口和其实现类时,使用了泛型(jdk5.0),在实例化集合对象时
如果没有泛型,则认为操作的默认为Object类型的数据
如果使用了泛型,则需要指明泛型的具体操作类型。一旦指明了泛型的具体类型,则在集合的相关方法中,只能添加此类型
//泛型在List中的使用 @Test public void test1(){ //举例:将学生成绩保存在ArrayList中 //标准写法: //ArrayList<Integer> list = new ArrayList<Integer>(); //jdk7的新特性:类型推断 ArrayList<Integer> list = new ArrayList<>(); list.add(56); //自动装箱 list.add(76); list.add(88); list.add(89); //当添加非Integer类型数据时,编译不通过 //list.add("Tom");//编译报错 Iterator<Integer> iterator = list.iterator(); while(iterator.hasNext()){ //不需要强转,直接可以获取添加时的元素的数据类型 Integer score = iterator.next(); System.out.println(score); } }
在哪里可以声明类型变量<T>
- 声明类或接口时,在类名或接口名后面声明泛型类型,我们把这样的类或接口称为泛型类或泛型接口。
【修饰符】 class 类名<类型变量列表> 【extends 父类】 【implements 接口们】{ } 【修饰符】 interface 接口名<类型变量列表> 【implements 接口们】{ } //例如: public class ArrayList<E> public interface Map<K,V>{ .... }
- 声明方法时,在【修饰符】与返回值类型之间声明类型变量,我们把声明了类型变量的方法,称为泛型方法。
[修饰符] <类型变量列表> 返回值类型 方法名([形参列表])[throws 异常列表]{ //... } //例如:java.util.Arrays类中的 public static <T> List<T> asList(T... a){ .... }
说明
① 我们在声明完自定义泛型类以后,可以在类的内部(比如:属性、方法、构造器中)使用类的泛型。
② 我们在创建自定义泛型类的对象时,可以指明泛型参数类型。一旦指明,内部凡是使用类的泛型参数的位置,都具体化为指定的类的泛型类型。
③ 如果在创建自定义泛型类的对象时,没有指明泛型参数类型,那么泛型将被擦除,泛型对应的类型均按照Object处理,但不等价于Object。
- 经验:泛型要使用一路都用。要不用,一路都不要用。
④ 泛型的指定中必须使用引用数据类型。不能使用基本数据类型,此时只能使用包装类替换。
⑤ 除创建泛型类对象外,子类继承泛型类时、实现类实现泛型接口时,也可以确定泛型结构中的泛型参数。
如果我们在给泛型类提供子类时,子类也不确定泛型的类型,则可以继续使用泛型参数。
我们还可以在现有的父类的泛型参数的基础上,新增泛型参数。
注意
① 泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如:<E1,E2,E3>
② JDK7.0 开始,泛型的简化操作:ArrayList<Fruit> flist = new ArrayList<>();
③ 如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象。
④ 不能使用new E[]。但是可以:E[] elements = (E[])new Object[capacity];
参考:ArrayList源码中声明:Object[] elementData,而非泛型参数类型数组。
⑤ 在类/接口上声明的泛型,在本类或本接口中即代表某种类型,但不可以在静态方法中使用类的泛型。
⑥ 异常类不能是带泛型的。
class Person<T> { // 使用T类型定义变量 private T info; // 使用T类型定义一般方法 public T getInfo() { return info; } public void setInfo(T info) { this.info = info; } // 使用T类型定义构造器 public Person() { } public Person(T info) { this.info = info; } // static的方法中不能声明泛型 //public static void show(T t) { // //} // 不能在try-catch中使用泛型定义 //public void test() { //try { // //} catch (MyException<T> ex) { // //} //} }
泛型在继承上的体现
① 类SuperA是类A的父类,则G<SuperA>与G<A>的关系,G<SuperA>和G<A>是并列的两个类,没有任何子类关系
比如:ArrayList<Object>、ArraryList<String>没有关系
② 类SuperA是类A的父类或接口,SuperA<G>与A<G>的关系:SuperA<G>与A<G>有继承或实现关系
比如:List<String>与ArrayList<String>
即类型必须保持一致
通配符 :?
使用说明
》举例:ArrayList<?>
》G<?>可以看成G<A>类型的父类,既可以将G<A>的对象赋值给G<?>类型引用(或变量)
读写数据特点(以集合ArrayList<?>为例)
》读取数据:允许的,读取的值的类型为Object类型
》写入数据:不允许,特例:但可以写入null值(因为类型未知<?>)
有限制条件的通配符
List<? extends A>:可以将List<A>或List<B>赋值给List<? extends A>,其中B类是A类的子类
List<? super A>:以将List<A>或List<B>赋值给List<? super A>,其中B类是A类的父类
有限制条件的通配符的读写条件