大家好,我是考100分的小小码 ,祝大家学习进步,加薪顺利呀。今天说一说java 泛型程序设计[亲测有效],希望您对编程的造诣更进一步.
泛型介绍
泛型程序设计 :可以被很多不同类型的对象所重用。比那些直接使用Object变量,然后强制类型转换的代码具有跟好的安全性和可读性。
使用类型参数可以将需要使用的类型,提前声明
ArrayList<String> newlist = new ArrayList<String>();
使用类型参数可以告知这个类适用于什么类型,当调用对应的get()方法的时候,不需要进行强制类型转换,编译器本事就知道其对应的类型。
当实现一个泛型的时候非常不容易,因为你需要知道这个这个类对应的所有用途及其类型,所以java提供了通配符类型,来解决这个问题。
定义简单泛型类
类型变量使用大写形式,且比较短,这是很常见的。在java库中,使用变量E表示集合的元素类型,K和V分别表示表的关键字和值的类型。T(需要时还可以用临近的字母U和S)表示类型。
泛型类,就是指具有一个或者多个类型变量,也就是说这个类适应这几种类型,对于之后在那类来说,我们只关注泛型,而不会为数据村吃的细节烦恼。
使用类型变量T,用<>括起来,放在类名后面。这个泛型可以有多个类型变量,如<T,U>
可以使用类定义的类型变量指定类中属性和方法的类型。
public class Pari<T> {
private T first;
private T second;
public Pari(){
first = null;
second = null;
}
public Pari(T first,T second){
this.first = first;
this.second = second;
}
public T getFirst(){
return first;
}
public T getSecond(){
return second;
}
public void setFirst(T value){
first = value;
}
public void setSecond(T value){
second = value;
}
}
Pair类引入了一个类型变量T,用尖括号(<>) 括起来, 并放在类名的后面;
泛型类可以有多个类型变量, 如, 定义 Pair 类, 其中第一个和第二个域使用不同的类型
其实泛型类可以看做是普通类的工厂。
泛型方法
泛型方法既可以在普通类中,也可以在泛型类中,定义方式是在方法名前加<T> T,说明该方法是泛型方法
class ArrayAlg
{
public static<T> T getMiddle(T... a)
{
return a[a.length / 2];
}
}
当调用一个泛型方法时, 在方法名前的尖括号中放入具体的类型
String middle = ArrayAlg.<String>getMiddle("john", "Q.", public ");
方法调用中可以省略 类型参数, 编译器有足够的信息能偶推断出所调用的方法;
也就是说, 可以调用
String middle = ArrayAlg.getMiddle("john", "Q.", public ");
double middle = ArrayAlg.getMiddle(3.14, 0, 1729);1
编译器将会自动打包参数为 1个 Double 和 2个Integer 对象,而后寻找这些类的共同超类型。
事实上, 找到2个这样的超类型:Number 和 Comparable 接口, 其本身也是一个泛型类型。 在这种情况下, 可以采取的补救措施是 将 所有的参数写为 double 值;
类型变量的限定
有的时候,比如对于特定的方法执行特定的操作,但是该操作不适用于一些类型,这时可以对类型变量T设置限定,可以使其集成特别的类或者接口(没错,在这里对于接口也是使用继承,因为使用extends更加接近子类的意思)
一个类型变量或通配符可以有多个限定,限定类型用“”&“” 分隔,而用逗号用来分隔类型变量。在java继承中,可以根据需要拥有多个接口超类型,但限定中至多有一个类。如果用一个类作为限定,但必须是第一个。
比如:T extends Comparable & Srializable
public static <T extends Comparable> Pari<T> getMinMax(T[] word){
if(word == null || word.length == 0)
return null;
T min = word[0];
T max = word[0];
for(int i=1;i<word.length;i++){
if(word[i].compareTo(max) > 0)
max = word[i];
if(word[i].compareTo(min) < 0)
min = word[i];
}
return new Pari<T>(min,max);
}
JVM中没有泛型,只有普通的类和方法
所有的类型参数都是用他们的限定类型转换(如果没有类型参数,则使用Object类型),这个过程称为擦除(erased),擦除类型变量,并替换为限定类型
有时为保持类型安全性,需要插入强制类型转换
约束与局限性
不能用基本类型实例化类型参数
不能用类型参数来代替基本类型。就是没有Pair<double>,只有Pair<Double>。当然主要是原因是类型擦除。擦除之后,Pair类含有Obkect类型的域,而Object不能存储double的值。
运行时类型查询只适用于原始类型
虚拟机中的对象总有一个特定的非泛型类型。因此,所有类型查询只产生原始类型。
如:if(a instanceof Pari<String>)是错误的,因为只能查询原始类型,即Pari,if(a instanceof Pari<T>)是错误的
又如:
Pari<String> pari1 = (Pari<String>) a
无论何时使用instanceod或者设计泛型类型的强制类型转换表达式都会看到一个编译器警告。
同样道理,getClass方法总是返回原始类型。
if (pari1.getClass() == pari2.getClass())返回true,因为两次getClass()都是返回Pari.class
不能创建参数化类型的数组
new Couple<Employee>[5] ;这种声明式不合法的,这里面有一个问题还是通过类型擦除机制来解释,类型擦除后couple的类型是Couple[],考虑一下两种赋值方式:
不能抛出也不能捕获泛型类实例
public class GenericException <T> extends Exception {...}
这种泛型类扩展子Throwable是不合法的,不能通过编译器。
public static <T extends Throwable> void doWork(Class<T> t) {
try {
// do work...
} catch (T e) {
e.printStackTrace();
}
} // 错误
而这个是合法的
public static <T extends Throwable> void doWork(T t) throws T {
try {
// do work...
} catch (Throwable e) {
e.printStackTrace();
throw t;
}
}// 正确
java 异常处理的一个基本原则是,必须为所有已检查异常提供一个处理器。不过可以利用泛型消除这个限制。
不能实例化类型变量
不能使用像new<T>(…),new t[…]或T.class这样的表达式中的类型变量。例如,下面的Pair<T>构造器就是非法的:
public Pair()
{
first =new T();
second=new T ();
}//ERROR
类型擦除将T改变成Object。而且本意肯定不希望调用newObject(),但是可以通过反射调用Class.newInstance方法来构造泛型对象。
遗憾的是T.class在Java中也是不被支持使用的,所以一种弥补的方式,传入一个类型阐述为T的Class对象,如Class<T>
public static <T> Couple<T> createInstance(Class<T> clazz) {
try {
return new Couple<T>(clazz.newInstance(), clazz.newInstance());
} catch (Exception e) {
return null ;
}
}
初学者对Java反射不熟悉不用着急,这里只要知道不能实例化类型参数即可,同理,不能实例化一个泛型数组,如
public static <T> T[] maxTwo(T[] values) {
T[] array = new T[2];
} // 错误
泛型构建数组是不合法的,因为这么构建在擦除之后构造的永远是new Object[2],这不是我们所希望的结果。而且这样会导致一些运行时错误。为了更进一步说明隐患问题,来看看下面代码:
public static <T extends Comparable<T>> T[] maxTwo(T[] array) {
Object[] result = new Object[2];
return (T[]) result; // Type safety: Unchecked cast from Object[] to T[]
}
这种方式会产生变异警告:Object[]转换为T[]是没有被检查的。我们来试一试这个调用: maxTwo(new String[] { “5”, “7” , “9” });,运行后,发生了类型转换异常,因为方法在调用的时候将Object[]转换为String[],失败的类型转化。怎么解决呢?同样这里可以使用Java发射来解决:
public static <T extends Comparable<T>> T[] maxTwo(T[] array) {
// Type safety: Unchecked cast from Object[] to T[]
return (T[]) Array.newInstance(array.getClass().getComponentType(), 2) ;
}
泛型类的静态上下文中类型变量无效
不能在静态域或方法中引用类型变量。
public class Singleton<T>
{
public static T singleInstacne;//ERROR
public static T getSingleInstance();//ERROR
{
if(singleInstacne==null)
return singleInstance;
}
}
如果这个程序能够运行,就可以声明一个Singleton<Random>共享随机数生成器,声明一个Singleton<JFileChooser>共享文件选择器对话框。但是,这个程序无法工作。类型擦除之后,只剩下Singleton类,他只包含一个singleInstance域。
注意擦除后的冲突
public class NameClash<T> {
public boolean equals(T value) {
return false ;
}
}
public
boolean equals(T value) {…},一个是从Object继承的
public
boolean equals(Object obj) {…},但类型擦除以后,前者方法成为了
public
boolean equals(Object value) {…},而在一个类中同时存在两个方法名和参数一样的方法是不可能的,所以这里引发的冲突是没法通过编译器的。可以通过重新命名方法进行修正。
class Calendar implements Comparable<Calendar> {...}
class GregorianCalendar extends Calendar implements Comparable<GregorianCalendar> {...}
类合成桥方法,实现了Comparable<Calendar>获得一个桥方法:
int compareTo (Object o) {
return compareTo((Calendar)o);}
int compareTo (Object o) {
return compareTo((GregorianCalendar )o);}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/13190.html