Java泛型之协变与逆变及extends与super选择

yizhihongxing

Java泛型中的协变与逆变是很重要的概念,也常被面试官问到。本篇文章将带你深入理解这些概念,并介绍有关extends和super的最佳实践。

什么是Java泛型中的协变和逆变?

在介绍协变和逆变之前,我们需要先了解两个术语: 子类型和超类型。子类型是指一个类派生自另一个类,而超类型是指另一个类派生自某一个类。这两个概念很重要,后面我们会经常用到。

在Java中,泛型支持协变和逆变。通俗点讲,协变是指“向上转型”,逆变是指“向下转型”。具体来说,协变支持超类型的引用指向子类型的对象,而逆变支持子类型的引用指向超类型的对象。这是因为Java泛型中,如果一个类是另一个类或接口的子类型,则相应的泛型也应该是可以兼容的。

Java泛型中extends和super的选择

Java中,有两种方法可以声明泛型类型的边界,即extends和super。extends表示泛型类型应该是其边界类型的子类型,而super表示泛型类型应该是其边界类型的超类型。在使用泛型时,我们需要根据具体情况来选择使用哪种方式。

使用extends

在定义泛型时,如果希望只接受某个类及其子类作为泛型类型参数,就可以使用extends关键字。例如:

public class Box<T extends Number> {
  private T t;

  public void set(T t) {
    this.t = t;
  }

  public T get() {
    return t;
  }
}

在这个Box类中,我们使用了extends关键字来限制泛型类型T必须是Number及其子类类型。这意味着,我们可以使用任何Number的子类实例来创建Box对象。例如:

Box<Integer> integerBox = new Box<Integer>();

extends关键字的使用场景包括:

  • 如果希望某个泛型类型参数只能接受其边界类型及其子类型。
  • 如果希望某个方法返回的类型是某个泛型类型参数的子类型。

使用super

和extends相反,如果希望只接受某个类及其超类作为泛型类型参数,就可以使用super关键字。例如:

public class Box<T super Number> {
  private T t;

  public void set(T t) {
    this.t = t;
  }

  public T get() {
    return t;
  }
}

在这个Box类中,我们使用了super关键字来限制泛型类型T只能是Number及其父类类型。这意味着,我们可以使用任何Number的父类实例来创建Box对象。例如:

Box<Object> objectBox = new Box<Object>();

super关键字的使用场景包括:

  • 如果希望某个泛型类型参数只能接受其边界类型及其超类。
  • 如果希望把某个泛型类型参数的超类型传递给某个方法。

两个示例说明

为了深入理解协变和逆变,我们来看两个示例。

示例1: 协变

假设我们有一个Shape类和一个Circle类,Circle类是Shape类的子类。现在我们需要将Circle类的实例添加到一个List中。我们可以这样定义这个List:

List<? extends Shape> shapes = new ArrayList<Circle>();

这个使用了extends关键字的定义表明,shapes可以指向一个List对象,但也可以指向一个List、List等其他类型的对象。这意味着,我们可以向shapes添加任何Shape类及其子类的实例。

shapes.add(new Circle());
shapes.add(new Shape());

示例2: 逆变

假设我们有一个Comparator接口,其中有一个compare方法:

public interface Comparator<T> {
  public int compare(T o1, T o2);
}

现在我们需要在一个GeometricShape类中实现一个Comparator接口的compare方法。由于Circle类是GeometricShape类的子类,我们希望能够使用一个比较圆形的比较器来比较GeometricShape类的实例。我们可以这样定义比较器:

public class CircleComparator implements Comparator<Circle> {
  public int compare(Circle c1, Circle c2) {
    // ...
  }
}

但这个比较器无法在GeometricShape类中实际使用,因为在Comparator接口和CircleComparator类之间不存在任何继承关系。为了解决这个问题,我们可以在GeometricShape类中使用一个接受Comparator<? super T>类型参数的sort()方法:

public abstract class GeometricShape {
  public abstract void draw();

  public static <T extends GeometricShape> void sort(List<T> shapes, Comparator<? super T> comparator) {
    // ...
  }
}

由于使用了super关键字,这个sort()方法可以接受任何比较器,只要它可以比较T的超类型。这意味着,我们可以这样调用我们的sort()方法:

GeometricShape.sort(shapes, new Comparator<Circle>() {
  public int compare(Circle c1, Circle c2) {
    // ...
  }
});

这里的sort()方法参数中,shapes是一个List,而Comparator的类型是Comparator<? super T>,T是GeometricShape。这是因为,我们希望sort()方法接受的比较器类型是GeometricShape的超类型,所以我们采用了super关键字。

结论

Java泛型中的协变和逆变是很重要的概念,也常被面试官问到。本篇文章介绍了什么是协变和逆变,并介绍了使用extends和super的最佳实践。请务必根据具体的场景,选择正确的关键字来定义泛型类型的边界。同时,也要时刻记得在使用协变和逆变时,尽可能地让泛型类型参数更加通用,以便兼容更多的子类型和超类型。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java泛型之协变与逆变及extends与super选择 - Python技术站

(0)
上一篇 2023年6月27日
下一篇 2023年6月27日

相关文章

  • Linux UDP服务端和客户端程序的实现

    下面是关于Linux UDP服务端和客户端程序的实现的详细攻略。 1.UDP简介 UDP(User Datagram Protocol)用户数据报协议是一种无连接的协议,与TCP协议不同,UDP不会建立连接,发送数据时不会保证数据的可靠性以及顺序,甚至不保证是否到达对方。UDP在实时数据传输中非常常见,例如视频流、音频流等。 2.UDP服务端程序实现 下面的…

    other 2023年6月27日
    00
  • 分享Android开发自学笔记之AndroidStudio常用功能

    分享Android开发自学笔记之AndroidStudio常用功能攻略 介绍 本攻略将详细讲解AndroidStudio中的常用功能,帮助您更好地进行Android开发。以下是一些示例说明。 1. 代码自动补全 AndroidStudio提供了强大的代码自动补全功能,可以大大提高编码效率。当您输入代码时,它会根据上下文和已有的代码提示您可能需要的代码片段。 …

    other 2023年8月25日
    00
  • ip和端口的相关检测

    IP和端口的相关检测 在网络通信中,我们经常需要检测IP和端口的可用性,以确保网络连接的稳定性和安全性。以下是IP和端口的相关检测的完整攻略。 步骤 以下是IP和端口的相关检测的步骤: 使用ping命令检测IP的可用性。 使用telnet命令检测端口的可用性。 示例 以下是两个示例,演示如何使用ping和telnet命令检测IP和端口的可用性。 示例1:使用…

    other 2023年5月6日
    00
  • js使用函数绑定技术改变事件处理程序的作用域

    当我们在JavaScript中编写事件处理程序时,通常会遇到一个问题:在事件处理程序内部,this关键字的值会指向触发事件的元素。然而,有时候我们希望在事件处理程序内部访问其他作用域中的变量或方法。这时,我们可以使用函数绑定技术来改变事件处理程序的作用域。 函数绑定技术可以通过bind()方法来实现。bind()方法会创建一个新的函数,该函数的this值被绑…

    other 2023年8月20日
    00
  • Linux 4.9内核正式发布!来看看更新了什么?

    Linux 4.9内核正式发布!来看看更新了什么? Linux 4.9内核是Linux内核的一个重要版本,它带来了许多新功能、改进和修复。下面是一些主要的更新内容: 1. 文件系统改进 Linux 4.9内核对文件系统进行了一些重要的改进。其中一个示例是对EXT4文件系统的改进。在新的内核版本中,EXT4文件系统现在支持更大的文件和分区大小。这意味着用户可以…

    other 2023年8月3日
    00
  • Python学习之面向对象编程详解

    Python学习之面向对象编程详解攻略 1. 理解面向对象编程的概念 在初学Python时,我们经常听到“面向对象编程”,但很少有人真正理解它的含义。面向对象编程(OOP)是一种编程方法,它将程序中的数据和方法组合成对象,并通过对象之间的交互来实现程序的功能。 OOP具有下面三个主要特性: 封装:将对象的状态和行为封装在一个单独的单元内,从而隔离了内部细节并…

    other 2023年6月27日
    00
  • python 内置错误类型 Built-in Exceptions

    Python 内置错误类型 Built-in Exceptions 在 Python 中,错误类型被定义为异常。每个异常都是一个类,这些类都是内置到 Python 中的。在程序执行过程中,当 Python 遇到错误时会自动抛出相应的异常。 以下是 Python 内置的一些常见异常及其描述: 1. Exception(所有异常的基类) 在 Python 中,所…

    其他 2023年3月28日
    00
  • Ubuntu系统U盘安装以及降内核

    下面是关于Ubuntu系统U盘安装以及降内核的完整攻略,包括基本概念、使用流程和两个示例等方面。 Ubuntu系统U盘安装 Ubuntu系统是一款基于Linux的操作系统,它可以通过U盘进行安装。下面是Ubuntu系统U盘安装的流程: 下载Ubuntu系统的ISO镜像文件; 准备一个至少8GB的U盘,并将其格式化为FAT32格式; 下载并安装一个U盘启动盘制…

    other 2023年5月6日
    00
合作推广
合作推广
分享本页
返回顶部