已复制
全屏展示
复制代码

Java 泛型机制理解与实践


· 4 min read

一. 泛型定义

泛型就是定义一种模板,例如ArrayList<T>,然后在代码中为用到的类创建对应的ArrayList<类型>,在Java标准库中的ArrayList<T>实现了List<T>接口,它可以向上转型为List<T> ,Java泛型的主要目的是实现代码的重用性和类型安全性。假如没有泛型,我们可能就会定义很多遍ArrayList了,比如 ArrayList<Integer>ArrayList<String>ArrayList<Float> 等等。

二. 使用泛型

2.1 泛型类

使用ArrayList时,如果不定义泛型类型时,泛型类型实际上就是Object,此时,只能把<T>当作Object使用,没有发挥泛型的优势。

List list = new ArrayList();            // 编译器警告:
list.add("Hello");
list.add("World");
String first = (String) list.get(0);    // 强制类型转换
String second = (String) list.get(1);   // 强制类型转换

当我们定义泛型类型<String>后,List<T>的泛型接口变为强类型List<String>

List<String> list = new ArrayList<String>();    // 无编译器警告
// List<String> list = new ArrayList<>();       // 或者省略后面的String,编译器能自动推断出泛型类型

list.add("Hello");
list.add("World");
String first = list.get(0);                     // 无强制转型
String second = list.get(1);                    // 无强制转型

2.1 泛型接口

除了ArrayList<T>使用了泛型,还可以在接口中使用泛型。例如Arrays.sort(Object[])可以对任意数组进行排序,但待排序的元素必须实现Comparable<T>这个泛型接口:

public interface Comparable<T> {
    /**
     * 返回-1: 当前实例比参数o小
     * 返回 0: 当前实例与参数o相等
     * 返回 1: 当前实例比参数o大
     */
    int compareTo(T o);
}

如下代码表示实现了Comparable<Person>接口的compareTo方法。

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        Person[] ps = new Person[] {
                new Person("Bob", 61),
                new Person("Alice", 88),
                new Person("Lily", 75),
        };
        Arrays.sort(ps);
        System.out.println(Arrays.toString(ps));
    }
}

class Person implements Comparable<Person> {
    String name;
    int score;
  
    Person(String name, int score) {
        this.name = name;
        this.score = score;
    }
  
    public int compareTo(Person other) {
        return this.name.compareTo(other.name);
    }
  
    public String toString() {
        return this.name + "--" + this.score;
    }
}

三. 自定义泛型

下面自定义了一个 Student 类,在创建 Student 对象的时候需要提供一个类型,看代码:

package org.example;


public class Main {
    public static void main(String[] args) {
        Student<String> stringStudent = new Student<String>("3");
        Student<Integer> integerStudent = new Student<Integer>(3);
        System.out.println("stringStudent = " + stringStudent);
        System.out.println("integerStudent = " + integerStudent);

        // anyTypeStudent 可以是 ?类型,此时相当于表示 Object
        Student<?> anyTypeStudent = new Student<String>("309");
        String grade = (String) anyTypeStudent.grade;
        System.out.println("grade = " + grade);

        Student.test1(99);
        integerStudent.test2(9.9);
    }

    public static class Student<T> {
        T grade;

        Student(T grade) {
            this.grade = grade;
        }

        @Override
        public String toString() {
            return "Student{" +
                    "grade=" + grade +
                    '}';
        }

        // 在方法定义前声明泛型
        public static <E> void test1(E e) {
            System.out.println(e);
        }

        // 在方法定义前声明泛型
        public <E> void test2(E e) {
            System.out.println(e);
        }

    }

}
自定义泛型执行结果

四. 泛型界限

package org.example;

public class Score<T extends Number> {   // 设定泛型上界,必须是Number的子类
    private T score;

    public Score(T score) {
        this.score = score;
    }

    public T getScore() {
        return score;
    }

    @Override
    public String toString() {
        return "Score{" +
                "score=" + score +
                '}';
    }

    public static void main(String[] args) {
        // 只要是Number的子类即可,不能是 String
        Score<Number> score1 = new Score<Number>(33);

        // score2 的 T 类型必须是Integer的父类
        Score<? super Integer> score2 = new Score<Number>(33);

        // tmp 的类型必须是 Integer 父类:Number 或者 Object
        Object tmp = score2.score;
    }
}

4.1 定义泛型界限

在定义泛型时,如果要定义的泛型不止这个类型,可能是这个类型的父类、子类,可以用泛型界限实现:

  • public class Score<T extends Number> {}  :T类型必须是Number的子类
  • public class Score<T super Integer> {} :T类型必须是Integer的父类

4.2 泛型引用界限

在定义泛型的引用时,也可以使用界限类型;

  • Score<? extend Integer> score1;    必须为 Integer 或者其子类。
  • Score<? super Integer> score2;      必须为 Integer 的父类。
🔗

文章推荐