持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第6天,点击查看活动详情
重点
当在声明类或接口时,类或接口中定义某个成员时,该成员有些类型是不确定的,而这个类型需要在使用这个类或接口时才可以确定,那么可以使用泛型
声明泛型类
语法格式
【修饰符】 class 类名<类型变量列表> 【extends 父类】 【implements 父接口们】{
}
public class NewClass<T> implements List<T>{}
复制代码
注意
<类型变量列表>
:可以是一个或多个类型变量,一般都是使用单个的大写字母表示,例如:<T>
、<K,V>
等<类型变量列表>
中的类型变量不能用于静态成员上
什么时候使用泛型类或泛型接口呢?
- 当某个类的非静态实例变量的类型不确定,需要在创建对象或子类继承时才能确定
- 当某个(些)类的非静态方法的形参类型不确定,需要在创建对象或子类继承时才能确定
泛型类注意事项
- 泛型类,如果没有指定具体的数据类型,此时,类型变量是
<Object>
- 泛型的类型变量只能是引用类型(Integer、Double),不能是基本数据类型(int、double)
- 泛型类型在逻辑上可以看成是多个不同的类型,但实际上都是同一个类
泛型类 🌰
需求背景
例如:要声明一个学生类,该学生包含姓名、成绩,而此时学生的成绩类型不确定
-
语文老师希望成绩是“优秀”、“良好”、“及格”、“不及格”
-
数学老师希望成绩是89.5, 65.0
-
英语老师希望成绩是'A','B','C','D','E'
-
那么在设计这个学生类时,就可以使用泛型
声明泛型类
public class Student<T>{
private String name;
private T score;
public Student() {
super();
}
public Student(String name, T score) {
super();
this.name = name;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public T getScore() {
return score;
}
public void setScore(T score) {
this.score = score;
}
@Override
public String toString() {
return "姓名:" + name + ", 成绩:" + score;
}
}
复制代码
使用泛型类
public class TestGeneric{
public static void main(String[] args) {
//语文老师使用时:
Student<String> stu1 = new Student<String>("张三", "良好");
//数学老师使用时:
//Student<double> stu2 = new Student<double>("张三", 90.5);//错误,必须是引用数据类型
Student<Double> stu2 = new Student<Double>("张三", 90.5);
//英语老师使用时:
Student<Character> stu3 = new Student<Character>("张三", 'C');
//错误的指定
//Student<Object> stu = new Student<String>();//错误的 因为类型不一样
}
}
复制代码
验证不同的泛型类型,都属于同一个类
@Test
void testStudent() {
//语文老师使用时:
Student<String> stu1 = new Student<String>("张三", "良好");
//数学老师使用时:
Student<Double> stu2 = new Student<Double>("张三", 90.5);
// 判断是否是同一个类
System.out.println(stu1.getClass() == stu2.getClass());
}
复制代码
重点
- JDK1.7 支持简写形式:
Student stu1 = new Student<>("张三", "良好");
- 指定泛型实参时,必须左右两边一致,不存在多态现象
从泛型类派生子类
两种情况
- 子类也是泛型类,子类和父类的泛型类型要一致
- 子类不是泛型类,父类要明确泛型的数据类型
子类也是泛型类
class ChildGeneric<T> extends Generic<T>
// 错误示范
// class ChildGeneric<E> extends Generic<T>
// class ChildGeneric<T> extends Generic<E>
复制代码
声明泛型子类、父类
/**
* 泛型父类
*/
class Parent<E> {
private E value;
public E getValue() {
return value;
}
public void setValue(E value) {
this.value = value;
}
}
/**
* 泛型类派生子类,子类也是泛型类,那么子类的泛型标识要和父类一致。
*
* @param <T> 子类、父类都得是 <T>
*/
class ChildFirst<T> extends Parent<T> {
@Override
public T getValue() {
System.out.println(super.getValue());
return super.getValue();
}
}
复制代码
使用泛型子类
@Test
void testFirst() {
ChildFirst<String> child = new ChildFirst<>();
child.setValue("字符串啊");
child.getValue();
}
复制代码
子类不是泛型类
class ChildGeneric extends Generic<String>
// 错误示范
// class ChildGeneric extends Generic<T>
复制代码
声明泛型子类
还是上面的父类
/**
* 泛型类派生子类,如果子类不是泛型类,那么父类要明确数据类型
*/
class ChildSecond extends Parent<Integer> {
@Override
public Integer getValue() {
System.out.println(super.getValue());
return super.getValue();
}
@Override
public void setValue(Integer value) {
super.setValue(value);
}
}
复制代码
使用泛型子类
@Test
void testSecond() {
ChildSecond child = new ChildSecond();
child.setValue(112233);
child.getValue();
}
复制代码
无需添加泛型实参
练习
- 声明员工类型 Employee,包含姓名(String),薪资(double),年龄(int)
- 员工类 Employee 实现 java.lang.Comparable 接口,指定 T 为 Employee 类型,重写抽象方法,按照薪资比较大小,薪资相同的按照姓名的自然顺序比较大小
- 在测试类中创建 Employee 数组,然后调用 Arrays.sort(Object[] arr) 方法进行排序,遍历显示员工信息
- 再次调用 Arrays.sort(Object[] arr,Comparator c) 方法进行按照年龄排序,年龄相同的安装姓名自然顺序比较大小,遍历显示员工信息
package basic_012;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
@NoArgsConstructor
@AllArgsConstructor
@Data
class Employee implements Comparable<Employee> {
private String name;
private double salary;
private int age;
// 重写抽象方法,按照薪资比较大小,薪资相同的按照姓名的自然顺序比较大小。
@Override
public int compareTo(@NotNull Employee o) {
if (this.getSalary() == o.getSalary()) {
// name是String类型,有compareTo方法
return this.getName().compareTo(o.getName());
}
return Double.compare(this.getSalary(), o.getSalary());
}
}
public class TPractice {
@Test
void test() {
Employee[] employees = new Employee[3];
employees[0] = new Employee("Irene", 18000, 18);
employees[1] = new Employee("Jack", 14000, 28);
employees[2] = new Employee("Alice", 14000, 24);
Arrays.sort(employees);
for (Employee employee : employees) {
System.out.println(employee);
}
}
@Test
void test2() {
Employee[] employees = new Employee[3];
employees[0] = new Employee("Irene", 18000, 18);
employees[1] = new Employee("Jack", 14000, 28);
employees[2] = new Employee("Alice", 14000, 24);
Arrays.sort(employees, (o1, o2) -> {
// 按照年龄排序,年龄相同的安装姓名自然顺序比较大小
if (o1.getAge() == o2.getAge()) {
return o1.getName().compareTo(o2.getName());
}
return Double.compare(o1.getAge(), o2.getAge());
});
for (Employee employee : employees) {
System.out.println(employee);
}
}
}
复制代码