什么是泛型
官方解释:泛型是jdk1.5中引入的一个新特性,本质是类型参数化,就是操作的数据类型被指定为一个参数,这种参数类型可以在类、接口和方法的创建中去确定。百度百科
泛型就是在我们定义方法和类中的属性时不指定类型,在方法和类使用时定义类型。程序在编译期检测类型是否正确。
泛型类
- 泛型类声明可以有一个类型参数,也可以有多个,
- 泛型类声明有一个类型参数部分,类型参数声明部分在类名之后的<>号中。
- 一个类可以有多个类型参数,多个类型参数要用逗号分隔。
- 类型参数用来接收一个或多个参数。(基本数据类型要使用包装类)
public class Box <T>{
private T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
这里在Box类后面 <>是泛型声明,<>号中的T就是泛型的类型参数,代指任意类型,这个T只是个代表不能确定类型。在创建对象时声明对象类型,对象是什么类型T就是什么类型。
public static void main(String[] args) {
//创建对象时指定类型可以是引用数据类型、包装类、自定义类
Box<String> n1 = new Box();//创建对象指定String类型
Box<Integer> n2 = new Box();//创建对象指定Integer类型
Box<Double> n3 = new Box();//创建对象指定Double类型
Box<Student> n4 = new Box();//创建对象指定自定义的student类型
Student student = new Student("张三", 18);
//对象元素赋值
n1.setT("aaaaaa");
n2.setT(123);
n3.setT(33.5);
n4.setT(student);
//查看对象元素
System.out.println(n1.getT());
System.out.println(n2.getT());
System.out.println(n3.getT());
System.out.println(n4.getT());
}
创建对象时指定对象类型,由于类中的成员变量和方法形参、返回值都是用类的类型参数,对象是什么类型,成员变量和方法的形参、返回值就是什么类型。也就是说类的成员变量类型、方法的形参类型和方法的返回值类型随着创建对象时声明的类型变化,可以操作任意类型的数据。
java 中泛型标记符:
- E - Element (在集合中使用,因为集合中存放的是元素)
- T - Type(Java 类)
- K - Key(键)
- V - Value(值)
- N - Number(数值类型)
- ? - 表示不确定的 java 类型
这些泛型标记符只是一种约定,并没有强制规定必须使用它们,你可以使用任何可以作为标识符的字符来表示类型参数。但是,为了代码的可读性和可维护性,建议使用这些标记符。
如果有多个泛型声明要用逗号隔开
public class Box <T,A,B>{
private T t;
private A a;
private B b;
public Box() {
}
public Box(T t, A a, B b) {
this.t = t;
this.a = a;
this.b = b;
}
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
}
这个类有3个类型参数,类中的成员变量可以使用这三个类型参数当作自己的类型。
你可以把它看成一个学生类,这个类的3个成员变量可以是:姓名(String)、年龄(Integer)、身高(Double),也可以是:姓名(String)、性别(String),学校(自定义类)
public static void main(String[] args) {
//对象有多个泛型参数
Box<String, Integer, Double> n = new Box();
//对象元素赋值使用set方法赋值
n.setT("张三");//姓名
n.setA(18);//性别
n.setB(173.5);//身高(单位cm)
//查看对象元素
System.out.println(n.getT());
System.out.println(n.getA());
System.out.println(n.getB());
System.out.println("-------------------------------------");
School school = new School("阳光小学", "5年1班");
Box<String, String, School> m = new Box<>("小明", "男", school);//使用构造方法赋值
System.out.println(m.getT());//姓名
System.out.println(m.getA());//年龄
System.out.println(m.getB());//所在学校
}
泛型方法
- 泛型方法声明和泛型类声明类似,访问修饰符后的<>是泛型声明,T是类型参数。返回值可以是void也可以是类型参数。
- 类型参数可以有多个,多个参数要用逗号分隔。
假设我们要对一个数组进行输出和查找最大值操作,
输出一个数组,这个数组可以是对整数,字符串,甚至是其他任意数据类型。(基本数据类型需要使用包装类)
public class Test {
//声明泛型方法,参数类型根据使用时传入的参数确定
public <T> void printArray(T[] arraylist){//打印数组
for (T i : arraylist){
System.out.print(i+" ");
}
System.out.println();
}
//声明泛型方法,参数类型继承Comparable接口,返回值为T
public <T extends Comparable<T>> T queryArray(T[] queryarray){
T t = queryarray[0];
for (T i : queryarray){
if (t.compareTo(i)<0){
t=i;
}
}
return t;
}
}
public static void main(String[] args) {
Integer[] integer = {23, 54, 33, 12, 77, 3, 60};
String[] strings = {"sadf", "aefsd", "zaaa", "eds", "gsdv"};
Test test = new Test();
test.printArray(integer);
System.out.println("最大值是:" + test.queryArray(integer));
System.out.println("------------------------------------------");
test.printArray(strings);
System.out.println("最大值是:" + test.queryArray(strings));
}
类型通配符
类型通配符使用?代替具体的类型参数。
类型通配符一般用在方法上。表示可以接受任意类型的泛型变量。
例如:
public static void query(ArrayList<?> list){
}
形参类型可以是 ArrayList < String > 、ArrayList< Integer>ArrayList< Number> 也可以是自定义类型的ArrayList
public static void main(String[] args) {
ArrayList<String> name = new ArrayList<>();
ArrayList<Integer> age = new ArrayList<>();
ArrayList<Number> number = new ArrayList<>();
name.add("aaaaa");
name.add("bbbbb");
age.add(101);
age.add(201);
number.add(102);
number.add(202);
query(name);
query(age);
query(number);
}
public static void query(ArrayList<?> list) {
for(int i = 0; i < list.size(); i++){
System.out.println(list.get(i));
}
}
由于无法确定传入对象的类型,query方法可以接收任意类型的ArrayList,并通过get方法获得内容。
想要限制传入的类型范围或者说想要将类型确定在一个范围之内,需要声明一个有界的类型参数(上限通配符、下限通配符)
上限通配符
首先列出类型参数的名称,后跟extends关键字,最后紧跟它的上界。
<? extends 类名> ?表示类型,extends后面的类名表示上界。即传入的类型必须是上限这个类或者这个类的子类。
public static void main(String[] args) {
ArrayList<String> name = new ArrayList<>();
ArrayList<Integer> age = new ArrayList<>();
ArrayList<Number> number = new ArrayList<>();
name.add("aaaaa");
name.add("bbbbb");
age.add(101);
age.add(201);
number.add(102);
number.add(202);
// query(name);
query(age);
query(number);
}
public static void query(ArrayList<? extends Number> list) {
for(int i = 0; i < list.size(); i++){
System.out.println(list.get(i));
}
}
由于query接收的类型只能是Number类或者是他的子类,query()传入其他类型会报错。
下限通配符
<? super 类名> ?表示类型,extends后面的类名表示上下限。即传入的类型必须是下限这个类或者这个类的父类直到object类。
public static void main(String[] args) {
ArrayList<Integer> age = new ArrayList<>();
ArrayList<Number> number = new ArrayList<>();
age.add(101);
age.add(201);
number.add(102);
number.add(202);
// query(age);
query(number);
}
public static void query(ArrayList<? super Number> list) {
for(int i = 0; i < list.size(); i++){
System.out.println(list.get(i));
}
}
由于query接收的类型下限是Number类,传入Integer类时会报错,因为Integer时Number的子类。