Java泛型
为什么需要泛型呢?
总结:泛型的作用是为了保证类型安全,如果集合中可以保存不同类型,那么有时候,比如比较的时候,不同类型是不同比较的,这样会报异常;
补充:Java中的泛型,只在编译阶段有效。在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段。
泛型类
public class Test { public static void main(String[] args) { A<String> a1 = new A<String>();//在new A对象指定泛型的类型String a1.setKey("aa"); // a1.setKey(1);//这个是编译器发现的问题 System.out.println(a1.getKey()); A<Integer> a2 = new A<Integer>();//在new A对象指定泛型的类型Integer a2.setKey(5);//对象使用setKey(T key)方法,中的key形参类型就是Integer System.out.println(a2.getKey());//T getKey(),返回值就由new对象确定返回值是Integer //没有定义泛型类型,默认Object,可以存储任何类型 A a3 = new A();//等同于 A<Object> a3 = new A<Object>(); a3.setKey("a");//setKey(value),value的类型已经是Object a3.setKey(32); a3.setKey(true); System.out.println(a3.getKey());//new A对象时就已经确定泛型的类型了 //同样的类,但是在new对象时泛型指定不同的数据类型,这些对象不能互相赋值 // a1 = a2;//会报错,a1的泛型是String,a2的泛型是Integer } } /** * @author leak * 泛型类 * 此次的泛型T可以任意的取名,A,B,V * 一般使用T,意为Type */ class A<T>{ private T key; // T类型由new对象时指定泛型,如果没有指定泛型,默认Object //如果指定了,比如泛型指定String,那么setKet/getKey返回值/形参类型是String public void setKey(T key) { this.key = key; } public T getKey() { return this.key; } }
总结:泛型类可以在创建对象时,指定任何泛型类型。
泛型接口
泛型接口和泛型类一样,在定义接口的时候,定义接口的泛型。注意如果在接口定义了泛型类型,但是实现该泛型接口的时候,实现类如果没有指定泛型类型,会报异常。
如果在实现接口的时候,指定了泛型类型,那么new对象时,就不需要额外的指定泛型类型,如果强硬指定泛型类型会报错。
public class Test2 { public static void main(String[] args) { //当new对象时指定泛型,泛型类里面的泛型类型也会全部跟着改变 B1<Object> b1 = new B1<Object>(); B1<String> b2 = new B1<String>(); B2 b3 = new B2();//B2实现类已经指定泛型类型了 // B2<String> b4 = new B2<String>();//强制指定泛型会报错,哪怕是指定相同类型 } } //接口指定泛型接口 interface IB<T>{ T test(T t); } //class B1 implements IB<T>{}//这种实现类实现泛型接口没有指定泛型的时候会报错,添加重写方法也没用 class B1<T> implements IB<T>{//实现类实现泛型接口必须指定泛型 @Override public T test(T t) { return t; } } /** * * @author leak * 如果实现接口时指定接口的泛型的具体数据类型 * 这个类实现接口所有方法的位置都要把泛型替换实际的具体数据类型 */ class B2 implements IB<String>{ @Override public String test(String t) { return t; } }
泛型方法
例子
/** * 泛型方法 * @param args */ public class Test3 { public static void main(String[] args) { Cc<Object> cc = new Cc<Object>(); cc.test("xx"); //泛型方法,在调用之前没有固定的数据类型 //在调用时,传入的参数是什么类型,就会把泛型改成什么类型 Integer test1 = cc.test1(2);//这里传了Integer泛型,那么泛型方法的返回值就是Integer Boolean test12 = cc.test1(true);//这里传了Boolean泛型,泛型方法的返回值为Boolean } } class Cc<E>{ private E e; //静态方法的泛型方法 public static <T> void test3(T t) { //在静态方法中,不能使用类定义泛型,如果要使用泛型,只能使用静态方法自己定义的泛型 // System.out.println(this.e);//这里会报错,不能使用类定义的泛型 System.out.println(t);//只能使用自己定义的泛型 } //无返回值的泛型方法 public <T> void test(T s) { //在类上定义的泛型,可以在普通的方法中使用 System.out.println(this.e); T t = s; } //下面都是有返回值的泛型方法 public <T> T test1(T s) { return s; } //形参可变参数的泛型方法 public <T>void test2(T...strs){ for(T s : strs) { System.out.println(s); } } }
总结:泛型方法,非静态方法可以调用泛型类的属性,静态方法如果使用泛型,那么必须是自己定义的泛型,不能调用类定义的泛型。
通配符
不确定集合中的元素具体的数据类型,使用?表示所有类型。
public void test(List<?> list){
System.out.println(list);
}
有限制的通配符
例子
import java.util.ArrayList; import java.util.List; public class Test4 { public static void main(String[] args) { List<String> l1 = new ArrayList<String>(); Dd d = new Dd(); d.test(l1); List<Integer> l2 = new ArrayList<Integer>(); d.test(l2); List<C> l3 = new ArrayList<C>(); List<D> l4 = new ArrayList<D>(); List<A1> l5 = new ArrayList<A1>(); //l6的泛型是IA接口 List<IA> l6 = new ArrayList<IA>(); //l7的泛型是IA接口的实现类 List<IAimpl> l7 = new ArrayList<IAimpl>(); //test1()方法都是约束了泛型为C及其子类 // d.test1(l2);//这里会报错,因为test1已经用通配符约束了泛型的类型范围,只能使用C及其子类的泛型 d.test1(l4);//l4为C类泛型的子类 d.test1(l3);//C类 //test2()方法约束了泛型为C及其父类 // d.test2(l4);//这里报错,因为l4的泛型类型不是C类的父类 d.test2(l3);//这里是C类 d.test2(l5);//l5的泛型是C类的父类 //test3()方法约束了泛型为:必须是实现了IA接口的实现类/AI接口,泛型指定其他类无效 d.test3(l6);//l6是IA接口泛型 d.test3(l7);//l7的泛型是实现IA接口的实现类 // d.test3(l5); // d.test3(l3);l5和l3都会报错,以为这两个集合的泛型都没有实现IA接口,也不是IA接口的泛型 } } class Dd{ //test方法需要一个list集合的参数,不确定list集合到底是存的类型是什么 //补充为什么不直接在形参定义List<T> list泛型呢,注意,在方法定义泛型和形参定义泛型是不同的概念 //如果你只需要定义形参的泛型,就使用通配符?,如果定义泛型方法,就在方法定义泛型 public void test(List<?> list) {} //约束泛型范围为C类及其子类 public void test1(List<? extends C> list) { System.out.println(list); } //约束泛型范围为C及其父类,也就是形参的泛型范围 public void test2(List<? super C>list) { System.out.println(list); } //约束接口/实现类 的泛型范围 public void test3(List<? extends IA> list) { System.out.println(list); } } class A1{} class B3 extends A1{} class C extends B3{} class D extends C{} //指定泛型为必须实现接口的实现类或者泛型是接口也行 interface IA{} class IAimpl implements IA{}
总结:当不知道集合中使用哪种数据类型时,可以用?通配符暂时代替;知道泛型范围,但是不知道具体泛型类型可以用”有限制通配符“约束泛型类型的范围。