在我看来,java的泛型一直就是语法糖,就是帮助我们在写代码的时候不会抛出java.lang.ClassCastException异常,看代码
1 public class GenericTest { 2 3 public static void main(String[] args) { 4 List list = new ArrayList(); 5 list.add("a"); 6 list.add("b"); 7 list.add(100); 8 9 for (int i = 0; i < list.size(); i++) { 10 //下面代码编译时肯定 没错,运行就会报错 11 String name = (String) list.get(i); 12 System.out.println("name:" + name); 13 } 14 } 15 }
你创建List的时候没有指定它的类型,那就是默认的Object,添加任何类型都没问题,遍历的时候要打印转换时,全部转String肯定报错,因为之前添加有int,所以这时候如果我们一开始加泛型,那添加的时候就会告诉我们,List里存储的是同一种类型
泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。再看代码
public class GenericTest { public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("a"); list.add("b"); // 提示编译错误 //list.add(100); for (int i = 0; i < list.size(); i++) { String name = list.get(i); // 这边就不需要转换,因为里面的类型都是String System.out.println("name:" + name); } } }
查看集合类的源码都是加泛型的,这样我们再执行一些操作的时候能确保集合里都是同一种“类型”
在泛型接口、泛型类和泛型方法的定义过程中,我们常见的如T、E、K、V等形式的参数常用于表示泛型形参,由于接收来自外部使用时候传入的类型实参。
知道这些后我们自己来实现一下泛型
1 public class Test { 2 3 public static void main(String[] args) { 4 5 //创建一个实体类,调用带参构造函数函数传个值 6 Container<String> name = new Container<String>("abc"); 7 //上面已经定义了Container里只能放String类型 8 System.out.println("name:" + name.getData()); 9 } 10 11 } 12 13 //自定义一个类,当作容器,里面可以放任何类型的实体 14 class Container<T> { 15 16 private T data; 17 18 public Container(T data) { 19 this.data = data; 20 } 21 22 public T getData() { 23 return data; 24 } 25 26 }
再看下面代码输出
1 Container<String> container1 = new Container<String>("a"); 2 Container<Integer> container2 = new Container<Integer>(100); 3 System.out.println(container1.getClass()); 4 System.out.println(container2.getClass()); 5 Boolean bl = container1.getClass() == container2.getClass(); 6 System.out.println(bl);
输出:
class Container
class Container
true
不管他们规定的是String类型还是Integer,编译过后的container1 和container2都是Container类型的,所以一开始说泛型只是语法糖~压根没有变
最后来看这段代码
class Test { public static void main(String[] args) { Container<String> name = new Container<String>("abc"); Container<Integer> age = new Container<Integer>(100); Container<Number> number = new Container<Number>(200); Container<Float> ft = new Container<Float>(200F); getData(name); getData(age); getData(number); // 报错 //getUpperNumberData(name); getUpperNumberData(age); getUpperNumberData(number); getUpperNumberData(age); //报错 //getDownNumberData(ft); } public static void getData(Container<?> data) { System.out.println("data :" + data.getData()); } public static void getUpperNumberData(Container<? extends Number> data) { System.out.println("data :" + data.getData()); } public static void getDownNumberData(Container<? super Integer> data) { System.out.println("data :" + data.getData()); } }
类型通配符一般是使用 “?”代替具体的类型实参。此处是类型实参,而不是类型形参。有时候类型通配符还分上限和类型通配符下限
类型通配符上限通过形如Container<? extends Number>形式定义,相对应的,类型通配符下限为Container<? super Number>形式,其含义与类型通配符上限正好相反