泛型的理解
-
泛型的概念
所谓泛型, 就是允许在定义类、 接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。 这个类型参数将在使用时(例如,继承或实现这个接口, 用这个类型声明变量、 创建对象时) 确定(即传入实际的类型参数, 也称为类型实参)
-
泛型的引入背景
集合容器类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象,所以在JDK1.5之前只能把元素类型设计为Object,JDK1.5之后使用泛型来解决。因为这个时候除了元素的类型不确定,其他的部分是确定的,例如关于这个元素如何保存,如何管理等是确定的,因此此时把元素的类型设计成一个参数,这个类型参数叫做泛型。Collection<E>,List<E>,ArrayList<E> 这个<E>就是类型参数,即泛型。
泛型在集合中的使用
-
栗子
栗子1
public void test1(){ ArrayList<Integer> list = new ArrayList<>(); list.add(23); list.add(54); list.add(31); Iterator<Integer> iterator = list.iterator(); while(iterator.hasNext()){ int i = iterator.next(); System.out.println(i); } }
栗子2
public void test2(){ HashMap<String, Integer> hashMap = new HashMap<String, Integer>(); hashMap.put("张三", 1001); hashMap.put("李四", 1002); hashMap.put("王五", 1003); //泛型的嵌套 Set<Map.Entry<String, Integer>> entries = hashMap.entrySet(); Iterator<Map.Entry<String, Integer>> iterator = entries.iterator(); while(iterator.hasNext()){ Map.Entry<String, Integer> entry = iterator.next(); String key = entry.getKey(); Integer value = entry.getValue(); System.out.println(key + ":" + value); } }
-
集合中使用泛型总结:
① 集合接口或集合类在jdk5.0时都修改为带泛型的结构。
② 在实例化集合类时,可以指明具体的泛型类型
③ 指明完以后,在集合类或接口中凡是定义类或接口时,内部结构(比如:方法、构造器、属性等)使用到类的泛型的位置,都指定为实例化的泛型类型。
比如:add(E e) —>实例化以后:add(Integer e)④ 注意点:泛型的类型必须是类,不能是基本数据类型。需要用到基本数据类型的位置,拿包装类替换
⑤ 如果实例化时,没指明泛型的类型。默认类型为java.lang.Object类型。
自定义泛型类、泛型接口、泛型方法
-
泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如:<E1,E2,E3>
-
泛型类的构造器如下: public GenericClass(){}
-
实例化后,操作原来泛型位置的结构必须与指定的泛型类型一致。
-
泛型不同的引用不能相互赋值
尽管在编译时ArrayList和ArrayList是两种类型,但是,在运行时只有一个ArrayList被加载到JVM中。
-
泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等价于Object。 经验: 泛型要使用一路都用。要不用,一路都不要用。
-
如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象。
-
jdk1.7,泛型的简化操作: ArrayList flist = new ArrayList<>();
-
泛型的指定中不能使用基本数据类型,可以使用包装类替换。
-
在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法中不能使用类的泛型。
-
异常类不能是泛型的
-
不能使用new E[]。但是可以: E[] elements = (E[])new Object[capacity];参考: ArrayList源码中声明: Object[] elementData, 而非泛型参数类型数组。
-
父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型:
子类不保留父类的泛型:按需实现
没有类型 擦除
具体类型子类保留父类的泛型:泛型子类
全部保留
部分保留
-
栗子
//使用T定义泛型类 public class Person<T>{ //使用T类型定义变量 private T info; //使用T类型定义一般方法 private T getInfo(){ return info; } //使用T类型定义构造器 public Person(T info){ this.info = info; } }
自定义泛型方法
-
方法,也可以被泛型化,不管此时定义在其中的类是不是泛型类。 在泛型方法中可以定义泛型参数,此时,参数的类型就是传入数据的类型。
-
泛型方法的格式:
[访问权限] <泛型> 返回类型 方法名([泛型标识 参数名称]) 抛出的异常
-
栗子
public <T> T get(int i, T t){ T result = null; return result; }
泛型在继承上的体现
-
如果B是A的一个子类型(子类或者子接口),而G是具有泛型声明的类或接口, G<B>并不是G<A>的子类型!
比如: String是Object的子类,但是List并不是List的子类。
通配符的使用
-
通配符的使用
- 使用类型通配符:?
比如: List<?> , Map<?,?>
List<?>是List、 List等各种泛型List的父类。 - 读取List<?>的对象list中的元素时,永远是安全的,因为不管list的真实类型是什么,它包含的都是Object。
- 写入list中的元素时,不行。因为我们不知道c的元素类型,我们不能向其中添加对象
唯一的例外是null,它是所有类型的成员。
将任意元素加入到其中不是类型安全的
另一方面,我们可以调用get()方法并使用其返回值。返回值是一个未知的类型,但是我们知道,它总是一个Object。
- 使用类型通配符:?
-
有限制的通配符
- <?>
允许所有泛型的引用调用
-
通配符指定上限
上限extends:使用时指定的类型必须是继承某个类,或者实现某个接口,即<=<? extends Number> (无穷小 , Number]
只允许泛型为Number及Number子类的引用调用
<? extends Comparable>
只允许泛型为实现Comparable接口的实现类的引用调用
-
通配符指定下限
下限super:使用时指定的类型不能小于操作的类,即>=<? super Number> [Number , 无穷大)
只允许泛型为Number及Number父类的引用调用
- <?>
----------------------------------------------华丽分割线---------------------------------------------------------
系统复习java第一弹,本文资源来源于尚硅谷公开课程:尚硅谷_Java零基础教程-java入门必备-初学者从入门到精通全套完整版(宋红康主讲)