JavaBase-泛型
为什么要有泛型
-
泛型:标签
-
泛型的设计背景
-
集合容器类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象,所以在JDK1.5之前只能把元素类型设计为Object,JDK1.5之后使用泛型来解决。因为这个时候除了元素的类型不确定,其他的部分是确定的,例如关于这个元素如何保存,如何管理等是确定的,因此此时把元素的类型设计成一个参数,这个类型参数叫做泛型。Collection,List,ArrayList 这个就是类型参数,即泛型
-
public interface Collection<E> extends Iterable<E> { }
-
泛型的概念
- 所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者更某个方法的返回值及参数类型。这个类型参数将在使用时(例如,继承或实现这个接口,用这个类型声明变量、创建对象时)确定(即传入实际的类型参数,也称为类型实参)
- 从JDK1.5以后,java引入了“参数化类型(Parameterized type)“的概念,允许我们在创建集合时再指定集合元素的类型,正如:List,这表明该List只能保存字符串类型的对象
- JDK1.5改写了集合框架中的全部接口和类,为这些接口、类增加了泛型支持,从而可以在声明集合变量、创建集合对象时传入类型实参
在集合中用泛型
-
集合接口或集合类在jdk5.0都修改为带泛型的结构
-
实例化集合类对象时,可以指明具体泛型的类型
-
指明完以后,在集合类或接口中凡是定义类或接口时,内部结构(比如:方法,属性,构造器等)使用到类的泛型的位置,都指定为实例化时的泛型类型
- 比如在List接口中 add方法,在声明时是add(E e) -->实例化后:add(Integer e)
-
泛型的类型必须是一个类(不能是基本数据类型),如果要使用到基本数据类型需要使用包装类
-
如果实例化时,没有指明泛型的类型,默认类型为java.lang.Object
-
在jdk1.5之前,使用Object根父类来实现定义容器的元素类型
-
存在的问题
- 可以添加任意类型的元素,数据类型不安全
- 在遍历时,容易产生类型转换异常,ClassCastException
-
@Test public void test() { ArrayList list = new ArrayList(); //需求存放学生成绩 list.add(78); list.add(76); list.add(89); list.add(60); list.add(91); //问题一:类型不安全 list.add("Tom"); for(Object score : list){ //问题二:强转时,可能出现ClassCastException int stuScore = (Integer)score; System.out.println(stuScore); } }
-
-
在jdk1.5之后,增加了泛型的概念
-
在编译时就保证了容器元素的类型,保证了数据类型的安全
-
遍历时,可以确定元素类型,无需再使用强转
-
//在集合中使用泛型的情况 @Test public void test1(){ ArrayList<Integer> list = new ArrayList<Integer>(); //需求存放学生成绩 list.add(78); list.add(76); list.add(89); list.add(60); list.add(91); //问题一:类型不安全 ,已经声明了泛型了,再添加其他类型的元素,编译不通过,编译时进行类型检查,保证数据的安全 // list.add("Tom"); //遍历时 无需强转 for(Integer score : list){ System.out.println(score); } }
-
泛型的嵌套
-
@Test public void test2(){ Map<String,Integer> map = new HashMap<String, Integer>(); map.put("Tom",87); map.put("Jerry",92); map.put("Jack",81); //泛型的嵌套 Set<Map.Entry<String, Integer>> entries = map.entrySet(); Iterator<Map.Entry<String, Integer>> iterator = entries.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); } }
-
自定义泛型结构
泛型类,泛型接口
-
自定义的泛型结构:泛型类,泛型接口,泛型方法
-
泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如:
-
package com.jl.java.base.generic; /** * @author jiangl * @version 1.0 * @date 2021/4/26 14:58 */ public class MyGeneric<E1,E2,E3> { private E1 name; private E2 age; private E3 address; public MyGeneric(E1 name, E2 age, E3 address) { this.name = name; this.age = age; this.address = address; } public E1 getName() { return name; } public void setName(E1 name) { this.name = name; } public E2 getAge() { return age; } public void setAge(E2 age) { this.age = age; } public E3 getAddress() { return address; } public void setAddress(E3 address) { this.address = address; } }
-
-
泛型类的构造器如下:public MyGeneric(E1 name, E2 age, E3 address) ;
- public MyGeneric<E1,E2,E3>(E1 name, E2 age, E3 address) 是错误的
-
实例化后,操作原来泛型位置的结构必须与指定的泛型类型一致。
-
泛型不同的引用不能相互赋值
- 尽管在编译时ArrayList和ArrayList是两种类型,但是,在运行时只有一个ArrayList被加载到JVM中。
-
泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等价于Object。注意:泛型要使用一路都用。要不用,一路都不用。
-
如果泛型类是一个接口或抽象类,则不可创建泛型类的对象。
-
jdk1.7,泛型的简化操作:ArrayList list = new ArrayList<>();
-
泛型的指定中不能使用基本数据类型,可以使用包装类替换。
-
在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法中不能使用类的泛型(因为静态方法早于对象的创建,所在类加载时并不能确定泛型类型,所以无法在静态方法中使用泛型)。
-
异常类不能是泛型的。
-
不能使用new E[]。但是可以:E[] elements = (E[]) new Object[capacity];
- 参考:ArrayList源码中声明:Object[] elementData,而非泛型参数类型数组。
-
父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型
-
子类不保留父类的泛型:按需实现
- 没有类型 擦除
- 具体类型
-
子类保留父类的泛型:泛型子类
- 全部保留
- 部分保留
-
结论:子类除了指定或保留父类的泛型,还可以增加自己的泛型
-
package com.jl.java.base.generic; /** * @author jiangl * @version 1.0 * @date 2021/4/26 14:43 */ public class Order<T> { String orderName; int orderId; //类的内部结构可以使用泛型 T orderT; public Order(String orderName, int orderId, T orderT) { this.orderName = orderName; this.orderId = orderId; this.orderT = orderT; } public T getOrderT() { return orderT; } public void setOrderT(T orderT) { this.orderT = orderT; } } /** * 子类不保留父类的泛型,没有指定类型,擦除泛型,使用Object */ class ChildOrder extends Order{ public ChildOrder(String orderName, int orderId, Object orderT) { super(orderName, orderId, orderT); } } /** * 子类不保留父类的泛型,指定具体类型 */ class ChildOrder1 extends Order<Integer>{ public ChildOrder1(String orderName, int orderId, Integer orderT) { super(orderName, orderId, orderT); } } /** * 子类保留父类的泛型:全部保留 * @param <T> */ class ChildOrder2<T> extends Order<T>{ public ChildOrder2(String orderName, int orderId, T orderT) { super(orderName, orderId, orderT); } } /** * 子类保留父类的泛型:全部保留,并增加自己的新的泛型 * @param <K> * @param <T> */ class ChildOrder3<K,T> extends Order<T>{ private K key3; public ChildOrder3(String orderName, int orderId, T orderT, K key3) { super(orderName, orderId, orderT); this.key3 = key3; } } /** * 子类保留父类的泛型:部分保留,并增加自己的新的泛型 * @param <S> * @param <T> */ class ChildOrder4<S,T> extends ChildOrder3<String,T>{ private S key2; public ChildOrder4(String orderName, int orderId, T orderT, String key3, S key2) { super(orderName, orderId, orderT, key3); this.key2 = key2; } }
-
-
自定义泛型类或者泛型接口,泛型方法。其中为泛型
-
/** * @author jiangl * @version 1.0 * @date 2021/4/26 14:43 */ public class Order<T> { String orderName; int orderId; //类的内部结构可以使用泛型 T orderT; public Order(String orderName, int orderId, T orderT) { this.orderName = orderName; this.orderId = orderId; this.orderT = orderT; } public T getOrderT() { return orderT; } public void setOrderT(T orderT) { this.orderT = orderT; } }
-
-
如果定义了泛型类,实例化没有指明类的泛型,则认为此泛型类型为Object类型(不推荐)
- 要求如果定义了类是带泛型的,建议实例化时要指明泛型
-
由于子类在继承带泛型的父类时,指明了泛型类型。则实例化子类对象时,不需要再指明泛型
-
/** * @author jiangl * @version 1.0 * @date 2021/4/26 14:54 */ public class SubOrder extends Order<Integer>{ public SubOrder(String orderName, int orderId, Integer orderT) { super(orderName, orderId, orderT); } public static void main(String[] args) { SubOrder subOrder = new SubOrder("order",1,1); } }
-
如果子类在继承带泛型的父类时,未指明泛型类型。则实例化子类对象时,需要指明泛型
-
/** * @author jiangl * @version 1.0 * @date 2021/4/26 14:54 */ public class SubOrder1<T> extends Order<T>{ public SubOrder1(String orderName, int orderId, T orderT) { super(orderName, orderId, orderT); } public static void main(String[] args) { SubOrder1<Integer> subOrder = new SubOrder1("order",1,1); } }
泛型方法
-
泛型方法在方法中出现了泛型的结构,泛型方法的泛型参数与类的泛型参数没有任何关系
-
泛型方法所属的类是否是泛型类,没有关系,不一定
-
泛型方法,在调用时确定指明泛型的类型。
-
泛型方法可以是静态的,因为泛型方法的泛型参数是在调用方法时确定的,并非是在实例化时确定的
-
package com.jl.java.base.generic; import java.util.ArrayList; import java.util.List; /** * @author jiangl * @version 1.0 * @date 2021/4/26 15:21 */ public class GenericFuncTest { public <J> List<J> get(J[] arr){ ArrayList<J> list = new ArrayList<J>(); for(J j : arr){ list.add(j); } return list; } public static void main(String[] args) { Integer[] arr = new Integer[]{1,2,3,3,5,1}; GenericFuncTest genericFuncTest = new GenericFuncTest(); List<Integer> integers = genericFuncTest.get(arr); for(Integer i : integers){ System.out.println(i); } } }
-
泛型在继承上的体现
-
如果类A是类B的父类,G和G是不具备子父类关系的
-
public void test1(){ Object obj = null; String str = null; obj = str; Object[] arr1 = null; String[] arr2 = null; arr1 = arr2; List<Object> list1 = null; List<String> list2 = null; //此时list1和list2的类型不具备子父类关系 // list1 = list2; }
-
-
补充:类A是类B的父类,A
和B 是有子父类关系的 -
public void test2(){ List<String> list1= null; ArrayList<String> list2 = null; list1 = list2; }
-
-
父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型
-
子类不保留父类的泛型:按需实现
- 没有类型 擦除
- 具体类型
-
子类保留父类的泛型:泛型子类
- 全部保留
- 部分保留
-
结论:子类除了指定或保留父类的泛型,还可以增加自己的泛型
-
package com.jl.java.base.generic; /** * @author jiangl * @version 1.0 * @date 2021/4/26 14:43 */ public class Order<T> { String orderName; int orderId; //类的内部结构可以使用泛型 T orderT; public Order(String orderName, int orderId, T orderT) { this.orderName = orderName; this.orderId = orderId; this.orderT = orderT; } public T getOrderT() { return orderT; } public void setOrderT(T orderT) { this.orderT = orderT; } } /** * 子类不保留父类的泛型,没有指定类型,擦除泛型,使用Object */ class ChildOrder extends Order{ public ChildOrder(String orderName, int orderId, Object orderT) { super(orderName, orderId, orderT); } } /** * 子类不保留父类的泛型,指定具体类型 */ class ChildOrder1 extends Order<Integer>{ public ChildOrder1(String orderName, int orderId, Integer orderT) { super(orderName, orderId, orderT); } } /** * 子类保留父类的泛型:全部保留 * @param <T> */ class ChildOrder2<T> extends Order<T>{ public ChildOrder2(String orderName, int orderId, T orderT) { super(orderName, orderId, orderT); } } /** * 子类保留父类的泛型:全部保留,并增加自己的新的泛型 * @param <K> * @param <T> */ class ChildOrder3<K,T> extends Order<T>{ private K key3; public ChildOrder3(String orderName, int orderId, T orderT, K key3) { super(orderName, orderId, orderT); this.key3 = key3; } } /** * 子类保留父类的泛型:部分保留,并增加自己的新的泛型 * @param <S> * @param <T> */ class ChildOrder4<S,T> extends ChildOrder3<String,T>{ private S key2; public ChildOrder4(String orderName, int orderId, T orderT, String key3, S key2) { super(orderName, orderId, orderT, key3); this.key2 = key2; } }
-
泛型的使用场景
-
抽象数据库操作公共行为,把方法逻辑和需要操作的数据类本身分离开。提高了复用性
-
例如:
-
创建一个公共的Dao接口,Dao提供了增删改查方法
-
/** * @author jiangl * @version 1.0 * @date 2021/4/26 15:34 */ public interface Dao<T>{ void add(T t); boolean update(T t); T get(int id); boolean remove(T t); }
-
创建一个Customer对象
-
package com.jl.java.base.generic.adv; /** * @author jiangl * @version 1.0 * @date 2021/4/26 15:36 */ public class Customer { private String name; private int id; public Customer(String name, int id) { this.name = name; this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } }
-
-
创建CustomerDao implements Dao
-
package com.jl.java.base.generic.adv; /** * @author jiangl * @version 1.0 * @date 2021/4/26 15:36 */ public class CustomerDao implements Dao<Customer>{ @Override public void add(Customer customer) { System.out.println("add:"+customer); } @Override public boolean update(Customer customer) { System.out.println("update:"+customer); return true; } @Override public Customer get(int id) { return new Customer("customer",1); } @Override public boolean remove(Customer customer) { System.out.println("update:"+customer); return true; } }
-
-
通配符使用
-
使用类型的通配符: <?>
- 比如:List<?>,Map<?,?>
- List<?>是List,List等各种泛型List的父类。
-
读取List<?>的对象list中的元素时,永远是安全的,因为不管list的真实类型是什么,它都包含的是Object
-
写入list<?>中的元素时,不行。因为不知道c的元素类型,不能添加对象
- 唯一例外的是null,它是所有类型的成员
-
类A是类B的父类,G和G是没有关系的,二者共同的父类时:G<?>
-
public void test3(){ List<String> list1= null; List<Object> list2 = null; List<?> list = null; list = list2; list = list1; }
-
-
对于List<?>不能向其内部添加数据,除了添加null之外
-
public void test3(){ List<String> list1= null; List<Object> list2 = null; List<?> list = null; list = list2; list = list1; List<String> list3 = new ArrayList<String>(); list3.add("dfadf"); list = list3; //list<?>不能添加数据 // list.add("dfafd"); //list<?>可以读取数据,读取到的类型是Object System.out.println(list.get(0)); }
-
-
有限制的通配符:
- <?> 允许所有泛型的引用调用
-
通配符指定上限
- 上限extends:使用时指定的类型必须是继承某个类,或者实现某个接口,即<=
-
通配符指定下限:
- 下限super:使用时指定的类型不能小于操作的类,即>=
-
举例
- <? extends Number> (无穷小,Number] - 只允许泛型为Number及Number子类的引用调用
- <? super Number>[Number,无穷大) - 只允许泛型为Number及Number父类的引用调用
- <? extends Comparable> - 只允许泛型为实现Comparable接口的实现类的引用调用
-
public static void main(String[] args) { List<? extends Person> list = null; List<? super Person> list2 = null; List<Student> list3 = null; List<Person> list4 = null; List<Object> list5 = null; list = list3; //不行 // list = list5; list = list4; list2 = list5; //不行 // list2 = list3; }