泛型的设计背景
- 集合容器类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象,所以在JDK1.5之前只能把元素类型设计为Object,JDK1.5之后使用泛型来解决。因为这个时候除了元素的类型不确定,其他的部分是确定的,例如关于这个元素如何保存,如何管理等是确定的,因此此时把元素的类型设计成一个参数,这个类型参数叫做泛型。Collection,List,ArrayList这个就是类型参数,即泛型。
泛型概念
所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这个类型参数将在使用时(例如,继承或实现这个接口,用这个类型声明变量、创建对象时)确定(即传入实际的类型参数,也称为类型实参)。
-
可以简单理解为:Java是强类型语言,为了增加适用性,泛型即为参数化类型。
-
泛型的本质就是"参数化类型"。一提到参数,最熟悉的就是定义方法的时候需要形参,调用方法的时候,需要传递实参。那"参数化类型"就是将原来具体的类型参数化
-
泛型的出现避免了强转的操作,在编译器完成类型转化,也就避免了运行的错误。
-
Java泛型也是一种语法糖,在编译阶段完成类型的转换的工作,避免在运行时强制类型转换而出现ClassCastException,类型转化异常。
Java泛型相对于Object类优势
Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常。同时,代码更加简洁、健壮。
泛型的使用
泛型类
泛型类概述:把泛型定义在类上
定义格式:
public class 类名 <泛型类型1,...> {
}
注意事项:泛型类型必须是引用类型(非基本数据类型)
泛型方法:普通或者静态方法都可以使用
注意:静态方法的泛型参数必须是自己独有,不能与泛型类的相同
泛型变量其实跟String,Integer,Double等等的类的使用上没有任何区别,T只是一个符号,可以代表String,Integer,Double……这些类的符号,在泛型函数使用时,直接把T看到String,Integer,Double……中的任一个来写代码就可以了。唯一不同的是,要在函数定义的中在返回值前加上标识泛型;
方法声明中定义的形参只能在该方法里使用,而接口、类声明中定义的类型形参则可以在整个接口、类中使用。当调用fun()方法时,根据传入的实际对象,编译器就会判断出类型形参T所代表的实际类型
注意:
- T的位置,在修饰符(此处为public static)后面;
- 泛型方法可以定义在泛型类中,也可以定义在普通类中;
- 调用泛型方法时,和构造一个具体的泛型类对象一样,在尖括号中要提供具体的类型
String middle = ArrayAlg.getMiddle(“john”, “Q.”, “Public”);
举例:
public class StaticFans {
//静态函数
public static <T> void StaticMethod(T a){
Log.d("harvic","StaticMethod: "+a.toString());
}
//普通函数
public <T> void OtherMethod(T a){
Log.d("harvic","OtherMethod: "+a.toString());
}
}
上面分别是静态泛型函数和常规泛型函数的定义方法,与以往方法的唯一不同点就是在返回值前加上来表示泛型变量。其它没什么区别。
使用方法如下:
//静态方法
StaticFans.StaticMethod("adfdsa");//使用方法一
StaticFans.<String>StaticMethod("adfdsa");//使用方法二
//常规方法
StaticFans staticFans = new StaticFans();
staticFans.OtherMethod(new Integer(123));//使用方法一
staticFans.<Integer>OtherMethod(new Integer(123));//使用方法二
-
方法一,可以像普通方法一样,直接传值,任何值都可以(但必须是派生自Object类的类型,比如String,Integer等),函数会在内部根据传进去的参数来识别当前T的类别。但尽量不要使用这种隐式的传递方式,代码不利于阅读和维护。因为从外观根本看不出来你调用的是一个泛型函数。
-
方法二,与方法一不同的地方在于,在调用方法前加了一个来指定传给的值,如果加了这个来指定参数的值的话,那StaticMethod()函数里所有用到的T类型也就是强制指定了是String类型。这是我们建议使用的方式。
泛型构造器
构造器也是一种方法,所以也就产生了所谓的泛型构造器。
和使用普通方法一样没有区别,一种是显示指定泛型参数,另一种是隐式推断
public class Person {
public <T> Person(T t) {
System.out.println(t);
}
}
使用:
public static void main(String[] args) {
new Person(22);// 隐式
new <String> Person("hello");//显示
}
泛型接口
泛型接口概述:把泛型定义在接口
定义格式:
public interface 接口名<泛型类型> {
}
实现子类:在子类的定义上也声明泛型类型
interface Info<T>{ // 在接口上定义泛型
public T getVar() ; // 定义抽象方法,抽象方法的返回值就是泛型类型
}
class InfoImpl<T> implements Info<T>{ // 定义泛型接口的子类
private T var ; // 定义属性
public InfoImpl(T var){ // 通过构造方法设置属性内容
this.setVar(var) ;
}
public void setVar(T var){
this.var = var ;
}
public T getVar(){
return this.var ;
}
};
public class GenericsDemo24{
public static void main(String arsg[]){
Info<String> i = null; // 声明接口对象
i = new InfoImpl<String>("李兴华") ; // 通过子类实例化对象
System.out.println("内容:" + i.getVar()) ;
}
};
如果现在实现接口的子类不想使用泛型声明,则在实现接口的时候直接指定好其具体的操作类型即可:
interface Info<T>{ // 在接口上定义泛型
public T getVar() ; // 定义抽象方法,抽象方法的返回值就是泛型类型
}
class InfoImpl implements Info<String>{ // 定义泛型接口的子类
private String var ; // 定义属性
public InfoImpl(String var){ // 通过构造方法设置属性内容
this.setVar(var) ;
}
public void setVar(String var){
this.var = var ;
}
public String getVar(){
return this.var ;
}
};
public class GenericsDemo25{
public static void main(String arsg[]){
Info i = null; // 声明接口对象
i = new InfoImpl("李兴华") ; // 通过子类实例化对象
System.out.println("内容:" + i.getVar()) ;
}
};