MST

星途 面试题库

面试题:Java泛型在集合框架中的通配符与边界

阐述Java集合框架中泛型通配符(?)的两种主要形式(上界通配符<? extends T> 和下界通配符<? super T>)的区别与适用场景。并结合实际代码示例,说明如何在方法参数、返回值等场景中正确使用它们来实现灵活且类型安全的集合操作。
47.1万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

区别

  1. 上界通配符<? extends T>
    • 表示类型的上界,即该通配符代表的类型是T或者T的子类。
    • 可以读取集合元素,但写入时受限,只能写入null。因为编译器无法确定具体的子类型,所以除了null,写入任何其他对象都可能导致类型不匹配。
  2. 下界通配符<? super T>
    • 表示类型的下界,即该通配符代表的类型是T或者T的超类。
    • 可以写入元素,因为T及其子类对象都可以安全地赋值给T的超类类型。但读取时受限,只能将读取的元素赋值给Object类型(因为具体超类类型不确定)。

适用场景

  1. 上界通配符<? extends T>
    • 适用于只需要从集合中读取数据的场景。例如,计算集合中元素的总和、平均值等操作。
  2. 下界通配符<? super T>
    • 适用于只需要向集合中写入数据的场景。例如,将一组特定类型的元素添加到集合中。

代码示例

  1. 上界通配符<? extends T>在方法参数中的使用
import java.util.ArrayList;
import java.util.List;

class Shape {}
class Circle extends Shape {}
class Rectangle extends Shape {}

public class UpperBoundWildcardExample {
    public static double calculateAreaSum(List<? extends Shape> shapes) {
        double sum = 0;
        for (Shape shape : shapes) {
            // 假设Shape有getArea方法
            sum += shape.getArea(); 
        }
        return sum;
    }

    public static void main(String[] args) {
        List<Circle> circles = new ArrayList<>();
        circles.add(new Circle());
        double circleAreaSum = calculateAreaSum(circles);

        List<Rectangle> rectangles = new ArrayList<>();
        rectangles.add(new Rectangle());
        double rectangleAreaSum = calculateAreaSum(rectangles);
    }
}
  1. 下界通配符<? super T>在方法参数中的使用
import java.util.ArrayList;
import java.util.List;

class Animal {}
class Dog extends Animal {}
class GoldenRetriever extends Dog {}

public class LowerBoundWildcardExample {
    public static void addDogs(List<? super Dog> dogsList, Dog dog) {
        dogsList.add(dog);
    }

    public static void main(String[] args) {
        List<Dog> dogs = new ArrayList<>();
        addDogs(dogs, new GoldenRetriever());

        List<Animal> animals = new ArrayList<>();
        addDogs(animals, new Dog());
    }
}
  1. 返回值场景
    • 上界通配符返回值示例
import java.util.ArrayList;
import java.util.List;

class Fruit {}
class Apple extends Fruit {}

public class UpperBoundReturnExample {
    public static List<? extends Fruit> getApples() {
        List<Apple> apples = new ArrayList<>();
        apples.add(new Apple());
        return apples;
    }

    public static void main(String[] args) {
        List<? extends Fruit> fruits = getApples();
        for (Fruit fruit : fruits) {
            // 处理水果
        }
    }
}
- **下界通配符返回值示例**:虽然使用较少,但如果有这样的需求,如下:
import java.util.ArrayList;
import java.util.List;

class Plant {}
class Flower extends Plant {}
class Rose extends Flower {}

public class LowerBoundReturnExample {
    public static List<? super Rose> getRoseContainer() {
        List<Flower> flowers = new ArrayList<>();
        return flowers;
    }

    public static void main(String[] args) {
        List<? super Rose> roseContainer = getRoseContainer();
        roseContainer.add(new Rose());
    }
}

通过上述示例,可以看到如何在不同场景中正确使用上界和下界通配符,实现灵活且类型安全的集合操作。