1.概述
把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊的类型。
优点:1.将运行期的错误提前到编译期提示,提高了安全性
2.省去强转的麻烦
由一个例子来引出泛型
ArrayList list=new ArrayList();
list.add("qwe");
list.add(123);
for(int i=0;i<list.size();i++){
String temp=(String)list.get(i);
System.out.println(temp);
}
报错:Exception in thread “main” java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at text.test.main(test.java:20)
由输出和报错可以看出,报错是在运行的时候出现,导致编写代码人员不知道代码当中出现错误,从而是代码安全性降低。从而出现泛型,将报错转换到编译期。这样我们就能及时发现问题。
2.泛型有以下几类
2.1泛型类
泛型类用于类的定义中,通过泛型可以完成对一组类的操作开放相同的接口,常用语典型的容器类当中,List、Set、Map
泛型类的定义格式:
class 类名<泛型类型1,…>
例子:
public class test {
public static void main(String[] args){
Generic<Integer> a=new Generic<Integer>();//泛型的使用
a.setT(123);
System.out.println(a.getT());
}
}
class Generic<T>{ //定义泛型类
private T key;
public T getT(){
return key;
}
public void setT(T key){
this.key=key;
}
}
输出:123
2.2泛型接口
与泛型类的定义和使用相似。
public interface Generator<T>{
public T next();
}
当实现泛型接口的类,没有传入泛型实参时:
//未传入实参时,与泛型类的定义一样,在声明类的时候,需将泛型的声明也一起加入到类中
class FruitGenerator<T> implements Generator<T>{
public T next() {
return null;
}
}
当实现泛型接口的类,传入泛型实参时:
//定义一个生产器实现这个接口,虽然只创建了一个泛型接口,但是我们可以为T传入无数个实参,形成无数种类型的接口
//在实现类实现泛型接口时,如已将泛型类型传入实参类型,则所有使用泛型的地方都要替换成传入的实参类型
public class FruitGenerator implements Generator<String> {
private String[] fruits = new String[]{"Apple", "Banana", "Pear"};
public String next() {
Random rand = new Random();
return fruits[rand.nextInt(3)];
}
}
2.3泛型方法
public class GenericTest {
//这个类是个泛型类,在上面已经介绍过
public class Generic<T>{
private T key;
public Generic(T key) {
this.key = key;
}
//我想说的其实是这个,虽然在方法中使用了泛型,但是这并不是一个泛型方法。
//这只是类中一个普通的成员方法,只不过他的返回值是在声明泛型类已经声明过的泛型。
//所以在这个方法中才可以继续使用 T 这个泛型。
public T getKey(){
return key;
}
/**
* 这个方法显然是有问题的,在编译器会给我们提示这样的错误信息"cannot reslove symbol E"
* 因为在类的声明中并未声明泛型E,所以在使用E做形参和返回值类型时,编译器会无法识别。
public E setKey(E key){
this.key = keu
}
*/
}
/**
* 这才是一个真正的泛型方法。
* 首先在public与返回值之间的<T>必不可少,这表明这是一个泛型方法,并且声明了一个泛型T
* 这个T可以出现在这个泛型方法的任意位置.
* 泛型的数量也可以为任意多个
* 如:public <T,K> K showKeyName(Generic<T> container){
* ...
* }
*/
public <T> T showKeyName(Generic<T> container){
System.out.println("container key :" + container.getKey());
//当然这个例子举的不太合适,只是为了说明泛型方法的特性。
T test = container.getKey();
return test;
}
//这也不是一个泛型方法,这就是一个普通的方法,只是使用了Generic<Number>这个泛型类做形参而已。
public void showKeyValue1(Generic<Number> obj){
Log.d("泛型测试","key value is " + obj.getKey());
}
//这也不是一个泛型方法,这也是一个普通的方法,只不过使用了泛型通配符?
//同时这也印证了泛型通配符章节所描述的,?是一种类型实参,可以看做为Number等所有类的父类
public void showKeyValue2(Generic<?> obj){
Log.d("泛型测试","key value is " + obj.getKey());
}
/**
* 这个方法是有问题的,编译器会为我们提示错误信息:"UnKnown class 'E' "
* 虽然我们声明了<T>,也表明了这是一个可以处理泛型的类型的泛型方法。
* 但是只声明了泛型类型T,并未声明泛型类型E,因此编译器并不知道该如何处理E这个类型。
public <T> T showKeyName(Generic<E> container){
...
}
*/
/**
* 这个方法也是有问题的,编译器会为我们提示错误信息:"UnKnown class 'T' "
* 对于编译器来说T这个类型并未项目中声明过,因此编译也不知道该如何编译这个类。
* 所以这也不是一个正确的泛型方法声明。
public void showkey(T genericObj){
}
*/
public static void main(String[] args) {
}
}