泛型和通配符详解

在 Java 中,**泛型(Generics)通配符(Wildcard)**是用于增强类型安全性和代码复用的强大特性。它们允许类、接口和方法在定义时不指定具体类型,而是在使用时指定类型参数。通过泛型和通配符,Java 可以在编译时进行类型检查,从而避免运行时错误。

下面我将详细解释这两个概念,包括其用法、区别、以及各种常见的模式。

1. 泛型(Generics)

泛型是一种在定义类、接口或方法时使用类型参数的机制,从而使得类、接口、方法能够在运行时处理不同类型的数据。泛型能够保证类型安全,避免类型转换错误,并提高代码的复用性。

1.1 泛型类

泛型类允许在类定义时使用占位符(类型参数),在使用类时提供具体类型。例如:

class Box {   // T 是类型参数
    private T value;

    public T getValue() {
        return value;
    }

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

在上面的例子中,Box 是一个泛型类,T 是类型参数。当你创建 Box 的实例时,可以指定具体的类型:

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

        Box stringBox = new Box<>();
        stringBox.setValue("Hello");
        System.out.println(stringBox.getValue());  // 输出: Hello
    }
}

此时 T 被替换成了具体的类型(如 IntegerString),保证了类型安全。

1.2 泛型方法

泛型不仅可以用于类,还可以用于方法。泛型方法允许在方法定义时使用类型参数,并且这些参数仅在该方法内有效。

public class Main {
    // 泛型方法
    public static  void printArray(T[] array) {
        for (T element : array) {
            System.out.print(element + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        Integer[] intArray = {1, 2, 3};
        String[] strArray = {"Hello", "World"};
        
        // 调用泛型方法
        printArray(intArray);  // 输出: 1 2 3
        printArray(strArray);  // 输出: Hello World
    }
}
1.3 泛型约束(边界)

有时你希望将泛型限定为某些类型的子类。Java 提供了 extends 关键字来对泛型进行约束,限定类型的上界(即该类型必须是某个类的子类或实现某个接口)。

// 泛型的上界,T 必须是 Number 或其子类
public class Main {
    public static  void printNumber(T number) {
        System.out.println(number);
    }

    public static void main(String[] args) {
        printNumber(10);     // 可以是 Integer
        printNumber(10.5);   // 可以是 Double
        // printNumber("Hello");  // 错误:不能是 String
    }
}

2. 通配符(Wildcard)

通配符是一种特殊的类型参数,通常用于泛型类型中,以表示任意类型。通配符可以简化泛型类型的使用,尤其在方法参数和泛型集合类型中,通配符常常被用来表示不确定的类型。

2.1 通配符的基本使用:?

? 是通配符的代表,表示不确定的类型。它通常与泛型一起使用,指代可以是任何类型的对象。

public class Main {
    public static void printList(List list) {
        for (Object obj : list) {
            System.out.println(obj);
        }
    }

    public static void main(String[] args) {
        List intList = List.of(1, 2, 3);
        List strList = List.of("a", "b", "c");
        
        printList(intList);  // 输出: 1 2 3
        printList(strList);  // 输出: a b c
    }
}

在上述例子中,List 表示可以是任何类型的列表,因此 printList 方法可以接受不同类型的列表。

2.2 通配符的上界:? extends T

? extends T 表示通配符的上界,意味着通配符表示的类型必须是 TT 的子类。通配符上界通常用于读取操作,比如从一个泛型容器中读取元素。

public class Main {
    public static void printNumbers(List list) {
        for (Number num : list) {
            System.out.println(num);
        }
    }

    public static void main(String[] args) {
        List intList = List.of(1, 2, 3);
        List doubleList = List.of(1.1, 2.2, 3.3);

        printNumbers(intList);    // 输出: 1 2 3
        printNumbers(doubleList); // 输出: 1.1 2.2 3.3
    }
}
2.3 通配符的下界:? super T

? super T 表示通配符的下界,意味着通配符表示的类型必须是 TT 的父类。通配符下界通常用于写入操作,比如将元素添加到一个泛型容器中。

public class Main {
    public static void addNumbers(List list) {
        list.add(10);  // 可以安全地添加 Integer 类型的元素
    }

    public static void main(String[] args) {
        List numList = new ArrayList<>();
        addNumbers(numList);  // 允许将 Integer 添加到 Number 类型的列表中

        // List objList = new ArrayList<>();
        // addNumbers(objList); // 错误:不能添加到 Object 类型的列表中
    }
}
 
  
2.4 通配符与泛型的结合

通配符与泛型的结合可以进一步增强代码的灵活性和可读性。例如,你可以声明一个方法,接受不同类型的集合,允许读取或写入其中的数据。

public class Main {
    public static void copyList(List src, List dest) {
        for (Number num : src) {
            dest.add(num);  // 将 src 中的元素添加到 dest 中
        }
    }

    public static void main(String[] args) {
        List intList = List.of(1, 2, 3);
        List numList = new ArrayList<>();
        
        copyList(intList, numList);  // 将 Integer 类型的元素复制到 Number 类型的列表中
    }
}

3. 泛型和通配符总结

  • 泛型

    • 提供类型安全,可以在编译时检查类型。
    • 允许在类、方法、接口中使用类型参数。
    • 可以通过 extends 关键字限定类型的上界,或者通过 super 关键字限定类型的下界。
  • 通配符

    • ? 表示任意类型。
    • ? extends T 表示类型是 TT 的子类,适用于读取操作。
    • ? super T 表示类型是 TT 的父类,适用于写入操作。

通过泛型和通配符,Java 提供了强大的类型灵活性和类型安全检查,使得开发者能够编写更高效、可复用、易维护的代码。

你可能感兴趣的:(java,开发语言)