泛型程序设计 :能够被不少不一样类型的对象所重用。比那些直接使用Object变量,而后强制类型转换的代码具备跟好的安全性和可读性。java
使用类型参数能够将须要使用的类型,提早声明数组
ArrayList<String> newlist = new ArrayList<String>();复制代码
使用类型参数能够告知这个类适用于什么类型,当调用对应的get()方法的时候,不须要进行强制类型转换,编译器本事就知道其对应的类型。安全
当实现一个泛型的时候很是不容易,由于你须要知道这个这个类对应的全部用途及其类型,因此java提供了通配符类型,来解决这个问题。
bash
类型变量使用大写形式,且比较短,这是很常见的。在java库中,使用变量E表示集合的元素类型,K和V分别表示表的关键字和值的类型。T(须要时还能够用临近的字母U和S)表示类型。dom
泛型类,就是指具备一个或者多个类型变量,也就是说这个类适应这几种类型,对于以后在那类来讲,咱们只关注泛型,而不会为数据村吃的细节烦恼。ui
使用类型变量T,用<>括起来,放在类名后面。这个泛型能够有多个类型变量,如<T,U>this
可使用类定义的类型变量指定类中属性和方法的类型。spa
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,用尖括号(<>) 括起来, 并放在类名的后面;
其实泛型类能够看作是普通类的工厂。设计
泛型方法既能够在普通类中,也能够在泛型类中,定义方式是在方法名前加<T> T,说明该方法是泛型方法code
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 对象,然后寻找这些类的共同超类型。
有的时候,好比对于特定的方法执行特定的操做,可是该操做不适用于一些类型,这时能够对类型变量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
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 ;
}
}
复制代码
class Calendar implements Comparable<Calendar> {...}
class GregorianCalendar extends Calendar implements Comparable<GregorianCalendar> {...} 复制代码