您现在的位置是:首页 > 文章详情

泛型总结

日期:2019-03-22点击:295
泛型

参与了孤尽大大的DIY班,这一期的主题时泛型,之前没有深入研究过泛型,于是有了此篇的总结。

1.简单的泛型类和接口
 public class GenericMemoryCell<AnyType>{ public AnyType read(){ return storedVal; } public void write(AnyType x){ storedVal = x; } private AnyType storedVal; }
 public interface Comparable<>{ public int comparaTo(AnyType other); }
从基础的泛型可以看出,限制了类和接口的参数的类型。这样一个直接的好处就是,时以前只有在运行时报告的错误,如今现在可以在编译时报错。 
2.菱形运算符
 //通过<>可以省略,自动匹配要生成的类的type GenericMemoryCell<Integer> m=new GenericMemoryCell<>();
3.带有限制的通配符

现在场景:totalArea是一个计算图形面积的静态方法,Shape是一个父类,Square,Circle继承了Shape的父类。

 public static double TotalArea(Colletion<Shape> arr){ double total=0; for(Shap s:arr){ if(s!=null) total+=s.area(); } return total; }

问题是,如果是,方法中传入,Colletion。编译通过,但是产生运行时错误。为了解决这个问题,java5采用了通配符表示参数类型的子类或者超类。Collection ,T IS-A Shape 。

 public static double TotalArea(Colletion<?extends Shape> arr){ double total=0; for(Shap s:arr){ if(s!=null) total+=s.area(); } return total; }
4.泛型方法

定义泛型方法时,必须在返回值前边加一个,来声明这是一个泛型方法,持有一个泛型T,然后才可以用T作为方法返回值
泛型方法要求的参数是Class类型,而Class.forName()方法的返回值也是Class,因此可以用Class.forName()作为参数。其中,forName()方法中的参数是何种类型,返回的Class就是何种类型。在本例中,forName()方法中传入的是User类的完整路径,因此返回的是Class类型的对象,因此调用泛型方法时,变量c的类型就是Class,因此泛型方法中的泛型T就被指明为User,因此变量obj的类型为User。
当然,泛型方法不是仅仅可以有一个参数Class,可以根据需要添加其他参数。
为什么要使用泛型方法呢?因为泛型类要在实例化的时候就指明类型,如果想换一种类型,不得不重新new一次,可能不够灵活;而泛型方法可以在调用的时候指明类型,更加灵活。

 public static <T> boolean contains(T [] arr,T x){ for(T val:arr){ if(x.equals(val)) return true; } return false; }
5.类型界限

我们想编写一个finMax的程序,比较的AnyType必须是实现comparable才可以比较。下面的这种方法是不行的

 public static <AnyType> AnyType findMax(AnyType [] arr){ int maxIndex=0; for(int i=0;i<arr.length;i++){ if(arr[i].compareTo(arr[maxIndex])) maxIndex=i; } return arr[maxIndex]; }

改写泛型,需要比较的泛型是可以比较的。所以就有
public static >
假设Shape实现了Comparable,Square继承了Shape,Square所继承的泛型是Comparable,并不是Comparable,但是这样也是可以的。所以,可以写成以下格式:

 public static <AnyType extends Comparable<? super AnyType>> AnyType findMax(AnyType [] arr){ int maxIndex=0; for(int i=0;i<arr.length;i++){ if(arr[i].compareTo(arr[maxIndex])) maxIndex=i; } return arr[maxIndex]; }
6.类型擦除

因为,泛型是java语言中的成分,而不是虚拟机中的结构。所以泛型类需要经过虚拟机的类型擦出而变成非泛型类。

编译器生成一个与泛型类名相同的原始类。类型变量使用它们原先的类型界限来代替。 
7.对于泛型的限制

由于类型擦除的存在,所以必须遵从以下规则:
1.基本类型不能做类型参数
2.instanceof检测和类型转换工作只对原始类型进行。

 这句话的理解,就是编译时检测,只对原始类型有用,进行转换之后就不管用了。 
 GenericMemoryCell<Integer> cell1=new GenericMemoryCell<>(); cell1.write(4); Object cell=cell1; GenericMemoryCell<String> cell2=(GenericMemoryCell<String>) cell; String s=cell2.read;

3.在一个泛型类中,static方法和static域均不可引用类型变量,因为再类型擦出后,类型变量就不存在了。
4.泛型类型的实例化

 //非法,如果T是一个类型变量 T obj=new T(); T [] arr=new T[10]

5.参数化类型的数组

如下方案例,正常情况,存在类型检查,但是类型擦除之后,cell和数组arr里面的内容同样都是GenericMemoryCell。只有在实际存储的时候会出现,ClassCastException错误。 
 GenericMemoryCell<String> [] arr= new GenericMemoryCell<String> [10]; GenericMemoryCell<Integer> cell1=new GenericMemoryCell<>(); cell1.write(4); Object [] arr2=arr; arr2[0]=cell String s=arr2[0].read();
8.函数对象

​ 定义一个没有数据而只有一个方法的类,并传递该类的一个实例。事实上,一个函数通过将其放在一个对象内部而被传递,这样的对象叫做函数对象。

 public static <T> T findMax(T [] arr,Comparator<? super T> cmp){ int maxIndex=0; for(int i=0;i<arr.length;i++){ if(cmp.compare(arr[i], arr[maxIndex])>0) maxIndex=i; } return arr[maxIndex]; } class CaseInsensitiveCompare implements Comparator<String>{ public int compare(String lhs,String rhs){ return lhs.compareToIgnoreCase(rhs); } } class TestProgram{ public static void main(String [] args){ String [] arr={"ZEBRA",“ali”,"arc"} System.out.println(findMax(arr,new CaseInsensitiveCompare())); } } //输出:ZEBRA最后
原文链接:https://yq.aliyun.com/articles/694979
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章