Java 泛型介绍

泛型是 Java 5 引入的一种强大的特性,它允许在类、接口和方法中使用类型参数来提高代码的复用性和类型安全。泛型的目标是实现代码的通用性,同时能够保证类型的安全性,避免强制类型转换错误。

1. 泛型的基本概念

泛型允许你定义类、接口或方法时指定类型参数,这样可以在创建对象时指定具体的类型。泛型的核心思想是 "类型参数化",通过在定义时使用占位符来表示某种类型,实际使用时可以指定具体类型。

泛型的基本语法:

class ClassName<T> {
    // T 代表类型参数,可以是任何类型
    private T value;
    
    public ClassName(T value) {
        this.value = value;
    }
    
    public T getValue() {
        return value;
    }
}

说明:

  • T 是一个类型参数(通常使用 TEK 等表示类型),它表示一种类型,可以是任何类型,T 只是一个占位符。

2. 泛型的用途

泛型的主要用途是实现类型安全代码复用。具体来说,泛型提供了以下好处:

  1. 类型安全:编译时检查类型错误,避免了运行时 ClassCastException
  2. 代码复用:通过类型参数化,可以实现与特定类型无关的代码,增加代码的通用性。

3. 泛型类

泛型类是指定义时使用类型参数的类。类可以定义一个或多个类型参数。

泛型类示例:

// 定义一个泛型类 Box,T 表示类型参数
class Box<T> {
    private T value;

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

    public T getValue() {
        return value;
    }
}

public class GenericClassExample {
    public static void main(String[] args) {
        Box<Integer> intBox = new Box<>();
        intBox.setValue(100);
        System.out.println(intBox.getValue());  // 输出: 100

        Box<String> strBox = new Box<>();
        strBox.setValue("Hello, Generic!");
        System.out.println(strBox.getValue());  // 输出: Hello, Generic!
    }
}

说明

  • Box<T> 是一个泛型类,可以在实例化时指定类型,如 Box<Integer>Box<String>
  • 泛型类的实例化时,T 被替换为实际的类型。

4. 泛型方法

除了泛型类,Java 还允许在方法中使用泛型。泛型方法是指方法中带有类型参数,方法的返回类型或参数类型是未知的,直到调用方法时才指定具体类型。

泛型方法示例:

public class GenericMethodExample {

    // 定义一个泛型方法,返回类型和参数类型均是泛型
    public static <T> void printArray(T[] array) {
        for (T element : array) {
            System.out.println(element);
        }
    }

    public static void main(String[] args) {
        Integer[] intArray = {1, 2, 3, 4};
        String[] strArray = {"Hello", "World"};

        printArray(intArray);  // 输出: 1 2 3 4
        printArray(strArray);  // 输出: Hello World
    }
}

说明

  • <T> 在方法签名中声明了泛型类型参数,表示该方法接受任意类型的数组,并打印出每个元素。
  • 泛型方法可以单独声明泛型,和类的泛型无关。

5. 泛型接口

接口也可以使用泛型参数。与泛型类类似,接口的泛型类型在实现该接口时会指定。

泛型接口示例:

// 定义一个泛型接口
interface Pair<K, V> {
    K getKey();
    V getValue();
}

class MyPair<K, V> implements Pair<K, V> {
    private K key;
    private V value;

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

    public K getKey() {
        return key;
    }

    public V getValue() {
        return value;
    }
}

public class GenericInterfaceExample {
    public static void main(String[] args) {
        MyPair<Integer, String> pair = new MyPair<>(1, "One");
        System.out.println("Key: " + pair.getKey() + ", Value: " + pair.getValue());  // 输出: Key: 1, Value: One
    }
}

说明

  • Pair<K, V> 是一个泛型接口,定义了两个类型参数 KV,用于表示键值对。
  • MyPair<K, V> 实现了这个接口,并提供了具体的实现。

6. 通配符(Wildcard)

在使用泛型时,有时我们并不关心具体类型,而只关心某个类型的上界或下界。此时可以使用通配符 ?

通配符的类型:

  1. 无上下界的通配符<?>,表示可以是任何类型。
  2. 上界通配符<? extends T>,表示 T 类型及其子类型。
  3. 下界通配符<? super T>,表示 T 类型及其父类型。

通配符示例:

import java.util.*;

public class WildcardExample {

    // 使用无上下界的通配符
    public static void printList(List<?> list) {
        for (Object obj : list) {
            System.out.println(obj);
        }
    }

    // 使用上界通配符
    public static void printNumbers(List<? extends Number> list) {
        for (Number num : list) {
            System.out.println(num);
        }
    }

    // 使用下界通配符
    public static void addNumbers(List<? super Integer> list) {
        list.add(1);
        list.add(2);
        System.out.println(list);
    }

    public static void main(String[] args) {
        List<Integer> intList = new ArrayList<>();
        intList.add(10);
        intList.add(20);

        List<Double> doubleList = new ArrayList<>();
        doubleList.add(10.5);
        doubleList.add(20.5);

        // 无上下界通配符
        printList(intList);  // 输出: 10 20
        printList(doubleList);  // 输出: 10.5 20.5

        // 上界通配符
        printNumbers(intList);  // 输出: 10 20
        printNumbers(doubleList);  // 输出: 10.5 20.5

        // 下界通配符
        List<Number> numList = new ArrayList<>();
        addNumbers(numList);  // 输出: [1, 2]
    }
}

说明

  • printList(List<?>):接受任何类型的 List
  • printNumbers(List<? extends Number>):接受 Number 类型或其子类类型的 List,如 IntegerDouble
  • addNumbers(List<? super Integer>):接受 Integer 类型或其父类类型的 List,可以向该列表中添加 Integer 类型或其子类。

7. 泛型的边界

  • 上界通配符(extends:指定泛型类型的上限,表示该类型必须是某个类型的子类。
  • 下界通配符(super:指定泛型类型的下限,表示该类型必须是某个类型的父类。

边界示例:

// 上界通配符:传入的类型必须是 Number 或其子类
public static void printNumbers(List<? extends Number> list) {
    for (Number num : list) {
        System.out.println(num);
    }
}

// 下界通配符:传入的类型必须是 Integer 或其父类
public static void addNumbers(List<? super Integer> list) {
    list.add(1);
    list.add(2);
    System.out.println(list);
}

8. 泛型的限制

  1. 不能创建泛型数组:例如,new T[10] 是不允许的。
  2. 不能使用泛型类型的 new 运算符:由于 Java 在编译时会进行类型擦除,无法知道实际的类型。
  3. 不能创建泛型类型的静态字段:因为泛型类型的信息会被擦除,不能用于静态字段。

9. 总结

特性 泛型类泛型方法通配符
类型参数 类定义时指定类型参数 T 方法签名时指定类型参数 <T> ? 表示任何类型,? extends T 表示 T 的子类型,? super T 表示 T 的父类型
用途 使类能处理不同类型的数据 使方法能够接受不同类型的参数 灵活处理不同类型的通配符
使用场景 创建通用类和集合类 创建通用的算法方法 处理不确定类型,限制类型的上下界

建议:

  • 使用泛型类和方法时,确保尽可能泛化代码,增加代码的复用性。
  • 使用通配符时要小心,避免引入类型不安全的问题。
Java碎碎念 文章被收录于专栏

来一杯咖啡,聊聊Java的碎碎念呀

全部评论

相关推荐

投递Adobe等公司10个岗位
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务