Java中的方差


Java凭借其强大的面向对象编程特性,为程序员提供了多种机制来开发灵活高效的代码。其中一个经常被忽视但至关重要的概念是方差。理解方差对于掌握Java至关重要,尤其是在使用泛型和集合时。本文深入探讨了Java中的方差,涵盖了它的类型——协变、逆变和不变——及其实际应用。

理解方差

方差指的是更复杂类型之间的子类型关系与其组成部分之间的子类型关系是如何关联的。简单来说,它决定了当这些类用作类型参数时,类的类型层次结构是如何保持的。在处理泛型时,方差变得尤为重要,它提供了一个框架来确保类型安全,同时允许在赋值中具有一定的灵活性。

方差可以分为三种主要类型

  • 协变如果ClassB是ClassA的子类,则Collection可以被视为Collection的子类.

  • 逆变 − 如果ClassB是ClassA的子类,则Collection可以被视为Collection的子类.

  • 不变 − Collection和Collection之间没有子类型关系,无论ClassA和ClassB之间是什么关系。

让我们更深入地研究每一个概念。

Java中的协变

Java中通过使用带有extends子句的通配符来实现协变。让我们考虑一个例子:

List<Animal> animals = new ArrayList<>();
<List<super Cat>cats=animals;

在这种情况下,您可以将Cat对象或其任何实例添加到cats中,但您不能从cats中读取并将结果视为Cat,因为它可能包含Cat的任何超类型,包括Animal或Object。因此,您可以写入cats,但不能以类型安全的方式从中读取。

Java中的不变

不变是Java中的默认行为,这意味着Collection和Collection之间没有子类型关系,无论ClassA和ClassB之间是什么关系。这看起来可能比较严格,但对于类型安全至关重要。在Java中,List不是List的子类型, even though String is a subtype of Object. This is because Java collections are mutable and allowing such a relationship would lead to runtime type errors.

List<String> strings = new ArrayList<>();
// Compile error: Incompatible types
List<Object> objects = strings;

In the above example, even though String is a subtype of Object, List is not a subtype of List, hence the compilation error.

This feature might initially appear as a limitation, but it's a vital aspect of Java's type system to ensure that no unsafe operations are performed. If List were a subtype of List, you could add an Object that is not a String into a List, leading to a ClassCastException at runtime.

List<String> strings = new ArrayList<>();
// If this were allowed...
List<Object> objects = strings;
// ...this would put a non-String into a List<String>
objects.add(new Object());
String str= strings.get(0); // ClassCastException

This example illustrates why it's essential to maintain invariance for type safety.

Bounded Type Parameters and Variance

Covariance and contravariance are most commonly used with bounded type parameters. Bounded type parameters are a way of indicating that a type parameter must be a subtype (extends keyword) or a supertype (super keyword) of a certain type. This allows for flexibility in what types can be passed to a method while still maintaining type safety.

For example, you might have a method that operates on lists of Number and all its subclasses −

public <T extends Number> void processNumbers(List<T> numbers) { /* ... */ }

In this method, T is a bounded type parameter that must be a Number or a subtype of Number. This allows the method to operate on List, List, List, etc., demonstrating covariance

Conclusion

In conclusion, understanding variance in Java is critical for effectively working with generics and collections. It allows for flexible code while ensuring type safety.

Covariance, using the extends keyword, allows a subclass to stand in for a superclass, enabling more generic object handling. Contravariance, on the other hand, permits the superclass to substitute for a subclass, enabling the execution of broader actions on more specific objects.

Invariance preserves type safety by ensuring that collections of different types, even if related by inheritance, maintain their uniqueness and prevent runtime type errors.

更新于:2023年7月19日

205 次浏览

开启你的职业生涯

完成课程获得认证

开始学习
广告