标签:指定 list zab 引用类型 img avg ext 信息 方法
就本质而言 “泛型”的意思就是参数化类型。参数化类型很重要,因为使用该特性创建的类、接口以及方法可以以参数的形式指定操作的数据类型。
泛型通俗的说就是方法的返回值或参数是不确定的,可以随创建该类对象时改变而改变。
泛型提供了以前缺失的类安全性,并且还可以简化处理过程(例如避免进行强制类型转换等),提高了代码的可重用性。
对于下面这段代码我们定义了一个泛型类,类中有一个私有的泛型成员变量和几个泛型方法。从中我们可以看出,成员变量或者方法的参数或者返回值可能不是肯定的而是T,并且T是可变的,我们称这个T为泛型。
main方法中分别使用这个泛型类操作了String 和 Integer 。如果没有泛型的支持,我们就只能写两个逻辑相同而参数不同的类或者进行强制转换,大大降低了代码重用和安全性。
1 class MyGeneric<T>{ //声明一个泛型类 2 private T obj;//可以使用泛型指定变量 3 public MyGeneric() {}; //无参构造 4 public MyGeneric(T val) { //泛型构造 5 this.obj = val; 6 } 7 public T getObj() { //返回值 8 return this.obj; 9 } 10 public String getName() { //得到确切类型 11 return this.obj.getClass().getName(); 12 } 13 14 } 15 16 public class TestGeneric { 17 public static void main(String[] args) { 18 MyGeneric<String> str = new MyGeneric<String>("xiaobai"); //操作String 19 System.out.println(str.getObj()); 20 System.out.println(str.getName()); 21 22 MyGeneric<Integer> integer = new MyGeneric<Integer>(5); //操作Integer 23 System.out.println(integer.getObj()); 24 System.out.println(integer.getName()); 25 } 26 }
1.泛型只是一个占位符,并没有实际意义,对于上面的例子,实际上操作的还是相应的数据类型(String和Integer)
2.泛型只能使用引用类型,不支持基本数据类型(至于上面第22行可以直接写5是因为自动装箱)
3.泛型引用之间是不兼容的,比如上面例子中的代码 str和integer两个实例对象是不兼容的(这是废话,泛型的目的就是提高通用性并限制操作类型)
java中允许一个类有多个泛型,泛型之间使用逗号隔开即可(参看Map<K,V>)。并且泛型只是一个占位符,可以用任何字符表示
1 class ManyGeneric<T,V,W>{ 2 private T obj1; 3 private V obj2; 4 private W obj3; 5 public T getObj1() { 6 return obj1; 7 } 8 public void setObj1(T obj1) { 9 this.obj1 = obj1; 10 } 11 public V getObj2() { 12 return obj2; 13 } 14 public void setObj2(V obj2) { 15 this.obj2 = obj2; 16 } 17 public W getObj3() { 18 return obj3; 19 } 20 public void setObj3(W obj3) { 21 this.obj3 = obj3; 22 } 23 24 }
不显式第指定泛型类型,而是使用 ?来表示泛型。当我们无法立即确定该类引用使用哪一个泛型(或至少可以确定他与某一个类的关系时)就使用这种形式。该通配一共有三种形式
1. <?> 形式: 不知道泛型到底是何形式·:使用这种方式时 就相当一 <? extends Object > 引用的实际对象只要是Object的子类就可以
ArrayList<?> list = new ArrayList<String> (); //正确 ArrayList<?> list = new ArrayList<Integer> (); //正确
2. <? extends xxx> 不知道具体是哪一种形式,但一定是xxx的子类 :引用的实际对象只能是 xxx类或xxx类的子类
ArrayList<? extends Number> list = new ArrayList<String> (); //错误 不是Number子类 ArrayList<? extends Number> list = new ArrayList<Integer> (); //正确
3. <? super xxx> 不确定哪一种形式,但一定是xxx的父类:引用的实际对象只能是 xxx 或 xxx的父类
ArrayList<? super Integer> list = new ArrayList<Number> (); //正确 ArrayList<? super Integer> list = new ArrayList<Object> (); //正确 ArrayList<? super Integer> list = new ArrayList<String> (); //错误 不是Integer的父类
可以使用任意参数来替换泛型类型是很好的,但是有一些时候我们可能希望对泛型类型进行一些限制。例如我们想在泛型中取得每一个值得double值,这个只有在类继承Number类才可以实现,否则就会编译错误。
那么这个时候我们就可以使用有界类型进行限定,要求泛型类型必须是Number类或其子类。
语法: T extends superClass 意思是说只允许superClass或者其子类的泛型。
见如下例子:
错误的代码:
1 class MyMathClass<T>{ 2 T[] nums; 3 public MyMathClass(T[] o) { 4 this.nums = o; 5 } 6 public double avg() { 7 double sum = 0.0; 8 for(int i=0;i<nums.length;i++) { 9 sum += nums[i].doubleValue(); //报错误 10 } 11 return sum/nums.length; 12 } 13 } 14 15 public class TestGeneric { 16 public static void main(String[] args) { 17 Integer[] nums = new Integer[10]; 18 for(int i=0;i<10;i++) { 19 nums[i] = (int)(Math.random()*100); 20 } 21 MyMathClass<Integer> test = new MyMathClass<Integer>(nums); 22 test.avg(); 23 } 24 }
这里的代码第9行报错,因为不是所有的类型都有doubleValue方法。但是只要是继承了Number类就会有这个方法,所以我们使用有界类型进行限定
限定方式 T extends Number
1 class MyMathClass<T extends Number>{ 2 T[] nums; 3 public MyMathClass(T[] o) { 4 this.nums = o; 5 } 6 public double avg() { 7 double sum = 0.0; 8 for(int i=0;i<nums.length;i++) { 9 sum += nums[i].doubleValue(); //报错误 10 } 11 return sum/nums.length; 12 } 13 } 14 15 public class TestGeneric { 16 public static void main(String[] args) { 17 Integer[] nums = new Integer[10]; 18 for(int i=0;i<10;i++) { 19 nums[i] = (int)(Math.random()*100); 20 } 21 MyMathClass<Integer> test = new MyMathClass<Integer>(nums); 22 System.out.println(test.avg()); 23 } 24 }
同理还可以使用多边界限定,当有多个边界的时候 使用&进行连接 :class MyMathClass<T extends Number & Serializable> 意思就是说只允许Number或其子类类型并且必须实现Serializable接口。
注意有界类型与泛型通配的区别 (有界类型是声明泛型类指定泛型范围,而泛型通配是泛型引用指向泛型对象时的限制)
由于要与以前的代码相兼容,java中的泛型是伪泛型,在编译器编译过程中将会擦除泛型的所有信息,使用确切的引用类型将其代替。这就意味着如果没有显示的确定类型,就将使用Object代替,然后进行强制转换来保证类型的匹配
这里就会发生一个模糊性问题,当有两个重载方法时,并且该方法唯一区别就是参数类型不同,则可能造成错误:
T obj = new T(); 是不合法的,因为编译器不知道要创建哪一种类型的对象,T只是一个占位符。
静态成员不能使用泛型声明参数,同时,静态方法也不能操作泛型参数,因为静态属性需要随类加载,无法判断当前泛型的类型。
1.不能实例化泛型数组 原因参考7.1
2.不能声明指向特定泛型类型的数组
泛型类不能继承Throwable类,这就意味着不能创建泛型异常类(也就是说泛型不支持异常,但是泛型类中依旧支持其他类型的异常)
当然,泛型支持异常也没有什么意义。
标签:指定 list zab 引用类型 img avg ext 信息 方法
原文地址:https://www.cnblogs.com/xiaobai1202/p/10850537.html