Java - 泛型类

持续创作,加速成长!这是我参与「掘金日新计划 · 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();
}
复制代码

无需添加泛型实参

练习

  1. 声明员工类型 Employee,包含姓名(String),薪资(double),年龄(int)
  2. 员工类 Employee 实现 java.lang.Comparable 接口,指定 T 为 Employee 类型,重写抽象方法,按照薪资比较大小,薪资相同的按照姓名的自然顺序比较大小
  3. 在测试类中创建 Employee 数组,然后调用 Arrays.sort(Object[] arr) 方法进行排序,遍历显示员工信息
  4. 再次调用 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);
        }
    }
}
复制代码
分类:
后端