java 泛型基础总结
一:为啥要使用泛型
解决元素存储的安全性问题
解决获取数据元素时,需要类型强转的问题
泛型,JDK1.5新加入的,解决数据类型的安全性问题,其主要原理是在类声明时通过一-个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这样在类声明或实例化时只要指定好需要的具体的类型即可。
Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ascastxcetion异常。同时,代码更加简洁、健壮。
二:使用泛型
三:泛型的几个重要使用
3.1在集合中使用泛型
1 @Test 2 public void test1()//在集合中未使用泛型,就可以添加任何类型,但是强转类型的时候容易发生异常 3 { 4 ArrayList arrayList = new ArrayList(); 5 arrayList.add(1); 6 arrayList.add("AAA");////1.没有使用泛型,任何Object及其子类的对象都可以添加进来 7 arrayList.add(1.2); 8 for (int i = 0; i < arrayList.size(); i++) { 9 int score = (Integer)arrayList.get(i);////2.强转为int型时,可能报ClassCastException的异常 10 System.out.println(score); 11 } 12 } 13 @Test 14 public void test2(){//在集合中使用泛型,限制了加入数据的类型,在编译是就可以判断类型正确与否 15 List<Integer> list = new ArrayList<Integer>(); 16 list.add(78); 17 list.add(87); 18 // list.add("AA");//直接在编译的时候就会显示错误 19 } 20 @Test 21 public void test3(){//在集合中使用泛型 22 Map<String ,Integer> map = new HashMap<>(); 23 map.put("AA", 78); 24 map.put("BB", 87); 25 map.put("DD", 98); 26 Set<Map.Entry<String, Integer>> entries = map.entrySet(); 27 Iterator iterator = entries.iterator(); 28 while (iterator.hasNext()){ 29 System.out.println(iterator.next());//整体输出 30 } 31 for (Map.Entry<String,Integer> o:entries){ 32 System.out.println(o.getKey()+"------->"+o.getValue()); 33 } 34 }
3.2自定义泛型类
3.2.1对象实例化时不指名泛型,默认为Object类型
3.2.2泛型不同的引用不能相互赋值
3.2.3加入集合的对象类型必须与指定的泛型类型一致
3.2.4静态方法中不能使用类的泛型
3.2.5如果泛型类是一个接口或抽象类,则不可以创建泛型类的对象,必须子类指明泛型的类别
3.2.6不能在catch中使用泛型
3.2.7从泛型类派生子类,泛型类型需要具体化
定义一个泛型类Order,泛型类型为T,一旦实例化对象指明泛型T的类别时,类中所有T就都确定了,
1 public class Order<T> { 2 private String orderName; 3 private int orderId; 4 private T t; 5 List<T> list = new ArrayList<>(); 6 public void add(){ 7 list.add(t); 8 } 9 public T getT(){ 10 return t; 11 } 12 public void setT(T t){ 13 this.t = t; 14 } 15 // public static T show(){//static方法属于类,不被实例化就可以加载,所以不能在static方法中使用泛型的声明 16 // System.out.println(t); 17 // } 18 // public void info() 19 // { 20 // try{//不可以在try-catch中使用类的泛型的声明 21 // 22 // }catch(T e){ 23 // 24 // } 25 // } 26 //声明一个泛型方法,这个类型不同于类的泛型,因此类实例化不会决定该类型,只会在调用的时候确定 27 public static <E> E getE(E e){ 28 return e; 29 } 30 //实现数组到集合的复制 31 public <E> List<E> fromArrayToList(E[]e,List<E> list){ 32 for (E e1:e){ 33 list.add(e1); 34 } 35 return list; 36 } 37 38 }
子类指明泛型的具体类型
1 //继承泛型或泛型接口时,可以指定泛型具体的类型 2 class SubOrder extends Order<Integer>{}
1 @Test 2 public void test4() 3 { 4 //1:当实例化泛型的对象时,指明泛型的类型 5 //指明以后,对应的泛型中所有使用泛型的位置,都变为实例化指定的类型 6 //2:如果我们自定义了泛型类,但是实例化时没有使用,那么默认时Object类的 7 Order<Boolean> booleanOrder = new Order<Boolean>(); 8 booleanOrder.add(); 9 System.out.println(booleanOrder.getT()); 10 booleanOrder.setT(true); 11 System.out.println(booleanOrder.getT()); 12 //当通过对象调用泛型方法时,指明泛型方法的类型 13 Integer i =booleanOrder.getE(23); 14 Double i1 =booleanOrder.getE(2.3); 15 Integer [] id = new Integer[]{1,2,34}; 16 ArrayList<Integer> arrayList = new ArrayList<>(); 17 booleanOrder.fromArrayToList(id,arrayList); 18 System.out.println(arrayList); 19 } 20 21 /** 22 * 泛型与继承的关系:若类A是类B的子类,那么List<A>就不是List<B>的子类 23 */ 24 @Test 25 public void test5(){ 26 Object object= null; 27 String str = ""; 28 object = str; 29 30 Object[] objects = null; 31 String[] strs = new String[]{"AA","BB","CC"}; 32 objects = strs; 33 34 List<Object> list = new ArrayList<>(); 35 List<String> list1 = new ArrayList<>(); 36 // list = list1 //Object是String的父类,,但是List<Object>和List<String>是同级,不能直接赋值 37 }
3.3泛型方法
3.4泛型与继承的关系
1 List<Object> list = new ArrayList<>(); 2 List<String> list1 = new ArrayList<>(); 3 // list = list1 //Object是String的父类,,但是List<Object>和List<String>是同级,不能直接赋值
下面是泛型使用在继承上的一个demo例子
DAO父类
package Generic; public class DAO<T> { public void add(T t){ //... } public T get(int index){ return null; } public void delete(int index){ } }
子类CustomerDAO继承父类,泛型类型是Customer
package Generic; public class CustomerDAO extends DAO<Customer>{ }
自定义类型Customer的实现
1 package Generic; 2 3 public class Customer { }
测试
1 package Generic; 2 3 public class TestCustomer { 4 public static void main(String[] args) { 5 CustomerDAO c = new CustomerDAO(); 6 c.add(new Customer()); 7 c.get(0); 8 } 9 }
3.5通配符
1 @Test 2 public void test7(){ 3 List<String> list = new ArrayList<>(); 4 list.add("AA"); 5 list.add("BB"); 6 List<?> list1 = new ArrayList<>(); 7 list1 = list; 8 Iterator<?> iterator = list1.iterator();//可以对通配符的对象赋值进行遍历 9 while (iterator.hasNext()){ 10 System.out.println(iterator.next()); 11 } 12 // list1.add("vv");//不允许向声明为通配符的集合类中调用add写入对象,唯一例外的是null 13 // list1.add(123); 14 list1.add(null); 15 }
@Test public void test6(){ List<?> list = null; List<Object> list1 = new ArrayList<>(); List<String> list2 = new ArrayList<>(); list = list1; list = list2; show(list1); // show(list2); show1(list1); show1(list); List<? extends Number> list3 = null; List<Integer> list4 = null; list3 = list4; // list3 = list1; List<? super Number> list5 = null; list5 = list1; }
3.6 类中的泛型方法
1 public class GenericFruit { 2 class Fruit{ 3 @Override 4 public String toString() { 5 return "fruit"; 6 } 7 } 8 9 class Apple extends Fruit{ 10 @Override 11 public String toString() { 12 return "apple"; 13 } 14 } 15 16 class Person{ 17 @Override 18 public String toString() { 19 return "Person"; 20 } 21 } 22 23 class GenerateTest<T>{ 24 public void show_1(T t){ 25 System.out.println(t.toString()); 26 } 27 28 //在泛型类中声明了一个泛型方法,使用泛型E,这种泛型E可以为任意类型。可以类型与T相同,也可以不同。 29 //由于泛型方法在声明的时候会声明泛型<E>,因此即使在泛型类中并未声明泛型,编译器也能够正确识别泛型方法中识别的泛型。 30 public <E> void show_3(E t){ 31 System.out.println(t.toString()); 32 } 33 34 //在泛型类中声明了一个泛型方法,使用泛型T,注意这个T是一种全新的类型,可以与泛型类中声明的T不是同一种类型。 35 public <T> void show_2(T t){ 36 System.out.println(t.toString()); 37 } 38 } 39 40 public static void main(String[] args) { 41 Apple apple = new Apple(); 42 Person person = new Person(); 43 44 GenerateTest<Fruit> generateTest = new GenerateTest<Fruit>(); 45 //apple是Fruit的子类,所以这里可以 46 generateTest.show_1(apple); 47 //编译器会报错,因为泛型类型实参指定的是Fruit,而传入的实参类是Person 48 //generateTest.show_1(person); 49 50 //使用这两个方法都可以成功 51 generateTest.show_2(apple); 52 generateTest.show_2(person); 53 54 //使用这两个方法也都可以成功 55 generateTest.show_3(apple); 56 generateTest.show_3(person); 57 } 58 }
对于静态方法要使用泛型的话,必须要将泛型定义在方法上
3.7 泛型数组
不能创建一个具体的泛型数组,因为jvm会在运行期间擦除泛型类型,可以用通配符,这样任意类型的都可以加入,不会出现错误。