Java泛型
泛型
1.为什么要使用泛型
集合容器类在设计阶段/声明阶段不能确定这个容器实际上存放的是什么类型的数据,在jdk5.0之前只能设计为Object类型,在jdk5,0之后使用泛型来解决。因为这个时候除了元素的类型是不确定的,其他部分是确定的,比如如何保存、如何管理等。因此此时把元素的类型设计为一个参数,这个类型参数就是泛型。如Collection<E>、List<E>...这里的<E>就是类型参数,即泛型。
2.在集合中使用泛型
- 集合接口或者集合类在jdk5.0开始都修改为带泛型的结构。
- 我们在实例化集合类时,可以指明具体的泛型类型,在指明完泛型类型之后,在集合类或者接口之中定义类或者接口,内部结构(方法、构造器、属性等)使用到类的泛型的位置,都指定为实例化的泛型类型。比如add(E e)------>add(Integer e)。
- 泛型的类型必须是一个类,不能是基本数据类型,如果需要使用基本数据类型,需要使用包装类替代。
- 如果实例化时没有指定泛型的类型,默认类型为java.lang.Object类型。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class GenericTest { @Test public void test(){ //注意:泛型的类型不能是基本数据类型 ArrayList<Integer> integers = new ArrayList<>(); integers.add(123); integers.add(123); integers.add(123); integers.add(123); integers.add(123); for (Integer integer : integers) { System.out.println(integer); } } @Test public void test1(){ Map<String,Integer> map=new HashMap<>(); map.put("Tom",120); map.put("Jerry",121); map.put("Mary",109); map.put("Jack",145); //泛型的嵌套 Set<Map.Entry<String, Integer>> entries = map.entrySet(); Iterator<Map.Entry<String, Integer>> iterator = entries.iterator(); while (iterator.hasNext()){ Map.Entry<String, Integer> next = iterator.next(); String key = next.getKey(); Integer value = next.getValue(); System.out.println("key="+key+"--->value="+value); } } }
3.自定义泛型结构
泛型类、泛型接口;泛型方法
泛型类可能有多个参数,多个参数放在尖括号内,中间使用逗号隔开。比如<E1,E2,E3...>
泛型类的构造器为:public GenericClass(){}
泛型的不同引用不能相互赋值。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
@Test public void test3(){ ArrayList<String> list1 = new ArrayList<>(); ArrayList<Integer> list2 = new ArrayList<>(); //list1=list2;不能相互赋值 }
泛型如果使用就一直使用,如果不用就一直不用。
如果泛型结构是接口或者抽象类,不能创建泛型对象
jdk7之后泛型可以简化ArrayList<Integer> list=new ArrayList<>():尖括号内的泛型类型可以省略
静态方法中不能使用类的泛型
异常类不能是泛型
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class MyError<T>{ private int id; private String name; //类的内部结构就可以使用类的泛型 private T myErrorT; public MyError() { } public MyError(String name, int id, T myErrorT) { this.name = name; this.id = id; this.myErrorT = myErrorT; } 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; } public T getMyErrorT() { return myErrorT; } public void setMyErrorT(T myErrorT) { this.myErrorT = myErrorT; } @Override public String toString() { return "MyError{" + "id='" + id + '\'' + ", name=" + name + ", myErrorT=" + myErrorT + '}'; } }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
@Test public void test2(){ //如果自定义了泛型类,实例化没有指明泛型的类型,则认为此泛型类型为object类型 //要求:如果定义了泛型类,建议在实例化时指明该泛型类型 MyError<String> myError = new MyError(); myError.setId(1); myError.setName("运行错误"); myError.setMyErrorT("方法XXXX中出现错误"); System.out.println(myError); }
泛型方法:
在方法中出现了泛型结构,泛型参数与类的泛型参数没有任何关系关系,换句话说,泛型方法与所属的类是不是泛型类毫无关系。
泛型方法可以声明为static,因为此时泛型参数是在调用方法时确定的,不是在实例化时确定的,和泛型类不同。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//泛型方法 public <E> List<E> getList(E[] arr){ ArrayList<E> list = new ArrayList<>(); for (E e:arr){ list.add(e); } return list; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
@Test public void test3(){ //泛型方法的调用 MyError<String> myError = new MyError<>(); Integer[] arr=new Integer[]{1,2,3,4,5,6}; //泛型方法在调用时,指明泛型参数arr的类型,与泛型类中的泛型参数(MyError<String>)无关; List<Integer> list = myError.getList(arr); System.out.println(list); }
4.泛型在继承上的体现
虽然类A是类B地父类,但是G<A>和G<B>不具备子父类关系,二者是并列关系。比如:List<Object> list1和List<String>list2,此时list2不能赋给list1.
但是A<G>和B<G>具有子父类关系,比如:List<String>和ArrayList<String>
5.通配符的使用
通配符:?
类A是类B地父类,但是G<A>和G<B>不具备子父类关系,二者是并列关系。二者共用的父类是G<?>.
对于通配符List<?> list不能在向其内部添加数据,除了null值。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public void test4(){ List<Object> list1=null; List<String> list2=null; //list1=list2; //通配符 List<?> list=null; list=list1; list=list2; //使用通配符之后,数据的读取和写入要求 list2.add("AA"); list2.add("BB"); list2.add("CC"); list2.add("DD"); //添加:此时list不能在向其内部添加数据。除了添加null值 //list.add("QQ"); //list.add(null); //获取(读取):允许读取数据,读取出的数据类型为Object Object o = list.get(0); System.out.println(o); }
有限制条件的通配符的使用
G<? ectends 类型A>:可以作为G<A>和G<B>的父类的,其中A是B的父类
G<? super类型A>:可以作为G<A>和G<B>的父类的,其中A是B的子类,此时可以写入数据,兼容A类型和其子类