Java 泛型介绍

114 阅读4分钟

Java学习资料

Java学习资料

Java学习资料


一、引言

在 Java 编程中,泛型是一个强大且重要的特性,它在 JDK 5.0 版本被引入。泛型的出现极大地增强了代码的类型安全性、可读性和可维护性,并且能够减少代码的重复。通过使用泛型,我们可以创建更加通用的类、接口和方法,这些通用的组件可以处理不同类型的数据,而不需要为每种数据类型都编写特定的代码。

二、泛型的基本概念

1. 泛型类

泛型类是在类定义时使用类型参数的类。类型参数是一种占位符,它在创建类的实例时会被具体的类型所替代。下面是一个简单的泛型类示例:

class Box<T> {
    private T content;

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

    public T getContent() {
        return content;
    }
}

在这个示例中,Box 类使用了类型参数 T。T 可以代表任何类型,当我们创建 Box 类的实例时,需要指定 T 具体代表的类型。例如:

Box<String> stringBox = new Box<>();
stringBox.setContent("Hello, World!");
String str = stringBox.getContent();
System.out.println(str);

这里,T 被具体化为 String 类型,因此 Box 实例只能存储 String 类型的对象。

2. 泛型接口

泛型接口的定义与泛型类类似,也是在接口定义时使用类型参数。例如:

interface Pair<K, V> {
    K getKey();
    V getValue();
}

可以实现这个泛型接口,如下:

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

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

    @Override
    public K getKey() {
        return key;
    }

    @Override
    public V getValue() {
        return value;
    }
}

使用时可以这样创建实例:

Pair<String, Integer> pair = new OrderedPair<>("one", 1);
System.out.println(pair.getKey() + ": " + pair.getValue());

3. 泛型方法

泛型方法是在方法定义时使用类型参数的方法。泛型方法可以在普通类或泛型类中定义。例如:

class Util {
    public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) {
        return p1.getKey().equals(p2.getKey()) && p1.getValue().equals(p2.getValue());
    }
}

调用泛型方法时,编译器会根据传入的参数类型自动推断类型参数。例如:

Pair<String, Integer> p1 = new OrderedPair<>("one", 1);
Pair<String, Integer> p2 = new OrderedPair<>("one", 1);
boolean result = Util.compare(p1, p2);
System.out.println(result);

三、泛型的类型通配符

1. 无界通配符

无界通配符使用 ? 表示,它可以匹配任何类型。例如,我们可以使用无界通配符来打印一个集合中的所有元素:

import java.util.ArrayList;
import java.util.List;

public class WildcardExample {
    public static void printList(List<?> list) {
        for (Object element : list) {
            System.out.print(element + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        List<Integer> intList = new ArrayList<>();
        intList.add(1);
        intList.add(2);
        intList.add(3);
        printList(intList);

        List<String> stringList = new ArrayList<>();
        stringList.add("hello");
        stringList.add("world");
        printList(stringList);
    }
}

2. 上界通配符

上界通配符使用 ? extends Type 表示,它表示该通配符所代表的类型是 Type 或 Type 的子类。例如,我们可以定义一个方法来计算数字列表的总和:

import java.util.ArrayList;
import java.util.List;

public class UpperBoundedWildcardExample {
    public static double sumOfList(List<? extends Number> list) {
        double sum = 0.0;
        for (Number num : list) {
            sum += num.doubleValue();
        }
        return sum;
    }

    public static void main(String[] args) {
        List<Integer> intList = new ArrayList<>();
        intList.add(1);
        intList.add(2);
        intList.add(3);
        double sum = sumOfList(intList);
        System.out.println("Sum: " + sum);
    }
}

3. 下界通配符

下界通配符使用 ? super Type 表示,它表示该通配符所代表的类型是 Type 或 Type 的父类。例如:

import java.util.ArrayList;
import java.util.List;

class Animal {}
class Dog extends Animal {}

public class LowerBoundedWildcardExample {
    public static void addDogs(List<? super Dog> dogList) {
        dogList.add(new Dog());
        dogList.add(new Dog());
    }

    public static void main(String[] args) {
        List<Animal> animalList = new ArrayList<>();
        addDogs(animalList);
    }
}

四、泛型的好处

1. 类型安全

泛型通过在编译时进行类型检查,避免了在运行时出现类型转换异常。例如,在使用泛型集合时,编译器会确保只能向集合中添加指定类型的元素。

2. 代码复用

泛型允许我们编写通用的代码,这些代码可以处理不同类型的数据,从而减少了代码的重复。例如,我们可以编写一个通用的排序算法,该算法可以对不同类型的数组进行排序。

3. 可读性增强

使用泛型可以使代码更加清晰和易于理解,因为类型信息在代码中是显式的。例如,List 明确表示该列表只能存储 String 类型的元素。

五、泛型的限制

1. 不能使用基本数据类型

泛型的类型参数必须是引用类型,不能是基本数据类型。例如,不能使用 Box,但可以使用 Box。

2. 运行时类型擦除

Java 的泛型是通过类型擦除实现的,在运行时,泛型类型信息会被擦除。这意味着在运行时无法获取泛型的具体类型参数。例如,List 和 List 在运行时是相同的类型。

总之,泛型是 Java 中一个非常重要的特性,它为我们提供了强大的类型安全和代码复用能力。通过合理使用泛型类、泛型接口、泛型方法和类型通配符,我们可以编写出更加健壮、高效和可维护的 Java 代码。同时,我们也需要了解泛型的限制,以便在实际开发中正确使用泛型。