泛型
1.概述
泛型,即“参数化类型”。就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式,然后在调用时传入具体的类型(把类型明确的工作推迟到创建对象或者调用方法的时候才去明确)。
2.使用
(1)泛型类
泛型类是有泛型这个特性的类,本质上还是一个类,所以它可以被继承。继承分为明确泛型类型和不明确泛型类型。
定义一个泛型类(可以定义多个泛型,之间用逗号隔开,<A,B,T,…>)
注意:类上声明的泛型只对类中的非静态成员有效
public class ClassName <T>{
private T data;
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
测试
public static void main(String[] args){
//这里的String就是实际类型参数
ClassName<String> name = new ClassName<>();
name.setData("泛型");
System.out.println(name.getData());
}
(2)泛型接口
定义一个泛型接口
public interface InterfaceName<T> {
T getData();
}
实现接口时,可以选择指定泛型类型,也可以选择不指定。
//指定类型
public class Demo1 implements InterfaceName<String>{
private String data;
@Override
public String getData() {
return data;
}
}
//不指定类型
public class Demo2<T> implements InterfaceName<T>{
private T data;
@Override
public T getData() {
return data;
}
}
(3)泛型方法
泛型是先定义,后使用,放在权限修饰符的后面。
private static <T> T 方法名(T a,T b){}
public class DemoTest {
public static void main(String[] args){
method("123","456");
}
public static <A> void method(A a,A b){
System.out.println(a+"----"+b);
}
}
(4)泛型限制类型
在使用泛型时,可以指定泛型的指定区域。
例如:必须时某某类的子类或者某某接口的实现类,格式:
<T extends 类或接口1 & 接口2>
//限定了是Person的实现类,如果不是就会报错
public class Demo4 {
public static void main(String[] args){
Room<Student> student = new Room<>();
Room<Nurse> nurse = new Room<>();
}
}
interface Person{}
class Student implements Person{}
class Nurse implements Person{}
class Room<T extends Person>{
T data;
}
(5)泛型中的通配符?
(5.1)<? extends Parent>,指定了泛型类型的上限
public class Demo4 {
public static void main(String[] args){
//指定上限Person
Room<? extends Person> student = new Room<Student>();
Room<? extends Person> nurse = new Room<Nurse>();
}
}
interface Person{}
class Student implements Person{}
class Nurse implements Person{}
class Room<T>{
T data;
}
(5.2)<? super Child> 指定了泛型类型的下限
public class Demo4 {
public static void main(String[] args){
//指定下限Student
Room<? super Student> student = new Room<Person>();
}
}
interface Person{}
class Student implements Person{}
class Nurse implements Person{}
class Room<T>{
T data;
}
(5.3)<?>,指定了没有限制的泛型类型。
public class Demo4 {
public static void main(String[] args){
//没有限制的泛型类型
Room<?> student = new Room<Person>();
Room<?> nurse = new Room<Nurse>();
}
}
interface Person{}
class Student implements Person{}
class Nurse implements Person{}
class Room<T>{
T data;
}
?通配符表示可以匹配任意类型的Java类。
注意:当我们使用?通配符的时候,只能调用对象与类型无关的方法,不能调用对象与类型有关的方法。那么换句话说,你不知道这个容器里面是什么类型,所以只能以Object的形式取出来。
在方法中,通配符的使用:
如果参数类型之间有依赖关系,或者返回值是与参数之间有依赖关系的。那么就使用泛型方法;如果没有依赖关系的,就使用通配符,通配符会灵活一些。
3.泛型转型
public class Demo4 {
public static void main(String[] args){
Room<Person> person = new Room<>();
Room<Nurse> nurse = new Room<>();
//person = nurse;
/*
引用person指向了Nurse,作为Person类型的引用person,看上去可以往里面加一个Student的。
但是person这个引用,实际上是指向的一个Nurse泛型的容器,如果能加进去,就
变成了Nurse泛型的容器里放进了Student,这就矛盾了。所以子类泛型不能转型为父类泛型
*/
//nurse = person;
/*
nurse实际指向的是泛型Person的容器,而这个容器里可能存放的是一个Student,而根据泛型
直接取出来就转成了Nurse,这也是矛盾的。所以父类泛型不能转型成子类泛型。
*/
}
}
interface Person{}
class Student implements Person{}
class Nurse implements Person{}
class Room<T>{
T data;
}
4.作用
(1)提高代码复用率。
(2)泛型中的类型在使用时指定,不需要强制类型转换(类型安全,编译器会检查类型)。
注意:
在编译之后程序会采取去泛型化的措施。也就是说Java中的泛型,只在编译阶段有效,在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段。