1.概念
形参:是声明函数时写的。实参:调用方法时传入的值。
泛型也叫‘参数化类型’,在不创建新类型的情况下,通过泛型指定不同类型来控制形参,具体限制类型。
在泛型的使用过程中,操作的数据类型被指定为一个参数类型。
例: List array = new ArrayList();
这时的array可以add字符串和数字类型。当使用时,都以String类型使用时就会报错。
List<String> array = new ArrayList<String>();
这时add类型为Integer时会报错。编译器会在变异阶段发现这种问题。
2.特性
泛型只在编译阶段有效,不会进入运行时阶段,即在add等操作的阶段。
List<String> a= new ArrayList<String>();
List<Integer> b= new ArrayList<Integer>();
Class classA = a.getClass();
Class classB = b.getClass();
classA.equals(classB) 输出 true;
所有泛型类型在逻辑上是多个不同类型,但事实都是相同的基本类型。
3.使用(泛型类、泛型接口、泛型方法)
a.泛型类
常见的泛型容器有List、Set、Map.
一个普通泛型类:
public class A<T>{
private T key;//这里的T类型由外部指派
}
当实例化或者调用时,泛型类的类参数只能是类(包括自定义类),不能是简单类型。
A<Integer> a = new A<Integer>();
当不传入类型实参时,在泛型类中,使用泛型方法和成员变量的类型可以是任意的。
A a1 = new A(111);
A a2 = new A("www");
A a3 = new A(true);
- *泛型类的类参数只能是类(包括自定义类),不能是简单类型。
- *不能对有确切泛型类型的泛型类进行instanceof操作。(即A不能,A能)
b.泛型接口
泛型接口和泛型类的使用和定义基本相同。
public interface Face<T>{
private T getKey();//这里的T类型由外部指派
}
//当实现泛型接口,传人实参时:方法中的T要和传入类型一致
public class FaceImpl implements Face<String>{
@override
public String getKey(){
return "111";
}
}
//当实现泛型接口,不传人实参时:声明类后面也要加<T>.
public class FaceImpl<T> implements Face<String>{
@override
public T getKey(){
return null;
}
}
! 泛型通配符
//Integer和Number是一个子类。
public void getValue(A<Integer> i){}
//当调用该方法时,传人参数是A<Number>类型的实参时,编译器会报错。
public void getValue(A<?> i){}
//**?** 只作为**类型实参**,并不是泛型方法。这样就可以传递不同的类型的参数。
c.泛型方法
泛型方法: 在调用泛型方法时需指明具体类型。
//只有声明了<T>才算泛型方法,传入类型实参也可以为T。
public <T> T getValue(A<T> A){return null;}
public <T,K> K getValue(User<T> t){return null;}//泛型的数量可以是多个的
//在A<T>类中下面的方法不是泛型方法,虽然其使用了了泛型,但是该泛型是已经声明过的泛型。其只是一个普通的成员方法。
private T name;
public T getName(T name){return null;}
//如果不定义name则上面的方法就是错误的。
类中的泛型方法:
public class user<T>{
//当泛型类中声明泛型方法<K>,因为泛型方法在声明的时候会声明<K>,所有即使user类没有声明,编译器也能正常编译。
public <K> void getName(K name){}//K是调用方法时要指明的具体类型,要在public和void之前声明
private T id;
public <T> void getId(T id){}
//前提:Apple是Fruits的子类;实例化apple
User<Fruits> user = new User<Fruits>();//实例化User;
//在调用该类方法时可以用user.getId(apple); 因为Apple是Fruits的子类
//调用getName时可以传人无关类People; user.getName(people);
}
泛型参数和可变参数
public <T> viod getName(T... names){
for(T name: names){
//这块可以打印出不同类型的name;
}
}
static方法无法访问泛型类型的参数,如果要使用泛型能力,则必须成为泛型方法。而且静态方法只能访问静态成员。
泛型的上下边界:
在泛型的使用过程中,可以限制其传入某种类型的父类或其子类。
public void getType(User<? extends Number>){}
//这样只能传入Number的子类
public class User<T extends Number>{}
User<String> user = new User<String>();//因String不是Number的子类实例化时会报错。
方法:权限 和 返回类型之间声明
public <T extends Number> T getName(User<T> user){}
泛型数组
首先,java中不能创建一个确切泛型类型的数组。
可以使用通配符来创建泛型数组。(再次说明 ? 是类型实参)
List<?>[] s = new ArrayList<?>[10]; //其中的 ?可以写成Integer、String等。
对泛型数组的声明进行限制,可以在编辑器提示代码有类型安全问题。