编程后端开发者

谈谈在Java中生成和使用泛型(generics)工作的关键方面,安全使用时需要注意哪些细节?

用 Hintsage AI 助手通过面试

回答。

泛型允许创建具有类型参数的类、接口和方法,这在编译时提供了类型检查,并帮助避免ClassCastException。

关键特性与潜在问题:

  • 类型擦除: 在编译时,类型参数的相关信息会被擦除,因此无法创建泛型类型的数组:new List<String>[10] — 编译错误。
  • 使用限制:
    • 不能创建参数化类型的实例: T obj = new T();
    • 不能使用instanceof与参数化类型: if(obj instanceof List<String>) — 错误。
    • 不能创建参数化类型的静态字段。
  • 通配符: ? extends T — 协变(读取),? super T — 逆变(写入)。
  • PECS原则(生产者扩展,消费者超限): 如果只需要读取—使用extends,如果需要写入—使用super。

示例:

// 协变的读取方式 void printNumbers(List<? extends Number> numbers) { for (Number n : numbers) { System.out.println(n); } } // 逆变的写入方式 void addIntegers(List<? super Integer> list) { list.add(10); }

反转的问题。

问题: "List<Object>和List<?>有什么区别?可以将任何对象放入List<?>吗?"

回答: 不,不能在List<?>中添加任何内容(除了null),因为编译器不知道具体的类型参数。而在List<Object>中可以添加任何对象。

示例:

List<?> list1 = new ArrayList<String>(); // list1.add("test"); // 编译错误! List<Object> list2 = new ArrayList<>(); list2.add("test"); // 正确

因为不了解主题细节而导致的真实错误示例。


故事

开发团队试图基于参数化类型的数组T[]实现缓存。由于类型擦除和无法创建泛型类型的数组,解决方案未按预期工作:得到了一个Object[]数组,导致在运行时进行类型转换时出现ClassCastException。


故事

在其中一个微服务中,开发者尝试实现一个使用List<?>作为参数的接收器,并试图修改集合。这导致了编译错误,并拖延了发布日期,因为需要根据PECS重构逻辑。


故事

在与外部系统的集成项目中,开发者犯了一个错误,通过未处理的原始类型覆盖了一种类型的集合:List list = new ArrayList<String>(),这导致ClassCastException,并在生产环境中尝试将元素转换为其他类型时服务崩溃。