Java 泛型

Java 5 引入的核心特性,允许在定义类、接口、方法时使用类型参数,带来了 编译时类型检查消除强制类型转换 两大好处。

为什么需要泛型?

  • 类型不安全:可以向集合中添加任意类型,导致运行时转型异常。
  • 代码繁琐:每次取出元素都需要手动强制转换。
// java5之前:
List list = new ArrayList();
list.add("hello");
list.add(123);        // 可以混入不同类型
String s = (String) list.get(0);  // 需要强制转换
String t = (String) list.get(1);  // 运行时抛出 ClassCastException

//java5之后
List<String> list = new ArrayList<>();
list.add("hello");
// list.add(123);     // 编译错误!只能添加 String
String s = list.get(0); // 自动转型,无需强制转换

基本语法

类型参数通常用单个大写字母表示(非强制,但为惯例)

  • E – Element(用于集合)
  • T – Type(任意类型)
  • K – Key(键)
  • V – Value(值)
  • N – Number
  • ? – 通配符(未知类型)

泛型类

静态成员不能使用类的泛型参数(因为静态成员在类加载时存在,而具体类型未确定)。

public class GenericClassDemo {

    // ========== 1. 单类型参数泛型类 ==========
    static class Box<T> {
        private T content;

        public void set(T content) {
            this.content = content;
        }

        public T get() {
            return content;
        }

        public boolean isEmpty() {
            return content == null;
        }

        @Override
        public String toString() {
            return "Box{" + content + '}';
        }
    }

    // ========== 2. 多类型参数泛型类 ==========
    static class Pair<K, V> {
        private K key;
        private V value;

        public Pair(K key, V value) {
            this.key = key;
            this.value = value;
        }

        public K getKey() {
            return key;
        }

        public V getValue() {
            return value;
        }

        public void setKey(K key) {
            this.key = key;
        }

        public void setValue(V value) {
            this.value = value;
        }

        @Override
        public String toString() {
            return "(" + key + ", " + value + ')';
        }
    }

    // ========== 3. 演示使用 ==========
    public static void main(String[] args) {
        // ----- 单类型参数 Box 的使用 -----
        Box<String> stringBox = new Box<>();
        stringBox.set("Hello, generics!");
        System.out.println("String box contains: " + stringBox.get());

        Box<Integer> intBox = new Box<>();
        intBox.set(100);
        System.out.println("Integer box contains: " + intBox.get());

        // ----- 多类型参数 Pair 的使用 -----
        Pair<String, Integer> agePair = new Pair<>("Alice", 28);
        System.out.println("Pair: " + agePair);
        System.out.println("Key: " + agePair.getKey() + ", Value: " + agePair.getValue());

        Pair<Double, Boolean> flagPair = new Pair<>(3.14159, true);
        System.out.println("Another pair: " + flagPair);

        // ----- 结合使用:将 Pair 放入 Box 中 -----
        Box<Pair<String, Integer>> boxWithPair = new Box<>();
        Pair<String, Integer> student = new Pair<>("Bob", 22);
        boxWithPair.set(student);
        System.out.println("Box containing a pair: " + boxWithPair.get());
        System.out.println("Unpacked pair key: " + boxWithPair.get().getKey());
        System.out.println("Unpacked pair value: " + boxWithPair.get().getValue());
    }
}

泛型接口

public interface Comparable<T> {
    int compareTo(T other);
}

// 实现时需要指定具体类型
public class Person implements Comparable<Person> {
    @Override
    public int compareTo(Person other) {
        return this.age - other.age;
    }
}

泛型方法

泛型方法可以定义在普通类或泛型类中,其类型参数位于返回值之前。

public class GenericMethodDemo {

    // 泛型方法,使用自己的类型参数 <T>
    public static <T> T getMiddle(T... a) {
        return a[a.length / 2];
    }

    public static void main(String[] args) {
        String middle = getMiddle("a", "b", "c"); // T 被推断为 String
        System.out.println(middle); // b
    }
}

类型擦除

Java 泛型是通过类型擦除实现的,即在编译后,类型参数会被替换为原始类型(Raw Type)。

  • 无边界类型参数替换为 Object
  • 有边界类型参数替换为第一个边界类型
List<String> list = new ArrayList<>();
// 编译后字节码中,List<String> 变为 List

影响

  • 运行时无法获取泛型的具体类型(例如 list instanceof List<String> 不合法)
  • 泛型类的静态变量在所有实例间共享(因为只有一份原始类)
  • 泛型不能用于基本类型(List<int> 错误,需使用包装类 List<Integer>

通配符— ?

当你不确定或不关心泛型的具体类型时,使用 ? 表示未知类型。

无限定通配符 ?

public static void printList(List<?> list) {
    for (Object obj : list) {  // 只能当作 Object 读取
        System.out.println(obj);
    }
    // list.add("hello");      // 编译错误!不能添加任何元素(null 除外)
}

List<?> 可以匹配任何类型的 List,但不能向其中添加元素(除了 null),因为类型未知,无法保证类型安全。

上界通配符? extends T

表示类型是 TT 的子类(T 为上界)。

public static double sumOfList(List<? extends Number> list) {
    double sum = 0.0;
    for (Number num : list) {
        sum += num.doubleValue();
    }
    return sum;
}
// 可以传入 List<Integer>, List<Double>, List<Number>
  • 可以读取T 类型(安全,因为任何子类都可视为 T)。
  • 不能写入(除了 null),因为具体子类型未知(例如不能往 List<? extends Number> 中添加 Integer,因为它可能实际是 List<Double>)。

下界通配符? super T

表示类型是 TT 的父类(T 为下界)。

public static void addNumbers(List<? super Integer> list) {
    list.add(123);       // 可以添加 Integer 及其子类(实际上 Integer 没有子类)
    list.add(456);
    // Integer obj = list.get(0); // 编译错误!读取时只能得到 Object
}
  • 可以写入 TT 的子类型(安全,因为 ? super T 一定能容纳 T)。
  • 读取时只能获取 Object,因为实际类型可能是 T 的任意父类。

PECS 原则

Producer Extends, Consumer Super

  • Producer:如果你只需要从集合中读取数据(生产),使用 ? extends T
  • Consumer:如果你只需要向集合中写入数据(消费),使用 ? super T
  • 同时读写则不应使用通配符,直接使用具体类型。
// 生产者:读取数据
public static <T> void copy(List<? extends T> src, List<? super T> dest) {
    for (T item : src) {
        dest.add(item);
    }
}

类型边界

可以限制类型参数必须是某个类的子类或实现某些接口。

// 限定 T 必须是 Number 的子类,并且实现了 Comparable
public static <T extends Number & Comparable<T>> T max(T[] arr) {
    T max = arr[0];
    for (T val : arr) {
        if (val.compareTo(max) > 0) max = val;
    }
    return max;
}
  • 使用 extends 关键字,后面可跟类或接口,用 & 连接多个接口。
  • 类必须放在第一位(如果有)。

泛型与继承

  • List<String> 不是 List<Object> 的子类型,即使 StringObject 的子类。
    否则以下代码会破坏类型安全:
  List<String> strings = new ArrayList<>();
  List<Object> objects = strings; // 假设合法
  objects.add(123); // 导致 strings 中含有 Integer
  • 但可以使用通配符实现协变:List<String>List<? extends Object> 的子类型。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇