首页 > 编程语言 >Java泛型中的PECS 原则

Java泛型中的PECS 原则

时间:2024-08-06 23:23:34浏览次数:14  
标签:PECS Java List ArrayList list 类型 编译器 泛型 集合

在 Java 泛型中,使用 extendssuper 关键字来定义通配符的上界和下界,主要是为了保证类型安全,并且能够灵活地处理不同类型的集合。具体来说,使用 extendssuper 的原因可以通过理解 PECS(Producer Extends, Consumer Super)原则来解释。

PECS 原则

  • Producer Extends: 如果需要一个只读的泛型集合(生产者),使用 extends
  • Consumer Super: 如果需要一个只写的泛型集合(消费者),使用 super

当使用泛型通配符 <? extends T> 时,集合被视为生产者,即我们可以从集合中读取数据,但不能向集合中添加数据。让我们深入了解这句话的含义。

理解 extends 用于生产者

使用 <? extends T> 通配符意味着这个集合可以是 T 类型或 T 的子类型。这种设计的目的是为了从集合中读取数据时保证类型安全,但是它也限制了向集合中添加数据的能力。

具体例子
import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<? extends Number> list = new ArrayList<Integer>();
        // list.add(1); // 编译错误
        Number num = list.get(0); // 可以读取数据
    }
}

为什么不能添加数据

当我们使用 <? extends T> 通配符时,编译器无法确定集合的具体类型。它只知道集合中的元素类型是 TT 的子类型,但是不能确定具体是哪一个子类型。由于这一点,向集合中添加元素会带来类型安全的问题。

编译器无法确定类型一致性

考虑以下代码:

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<? extends Number> list = new ArrayList<Integer>();
        // 编译器不知道 list 是什么类型的子类型
        // list.add(1); // 编译错误
    }
}

在这个例子中,list 的实际类型是 ArrayList<Integer>,但是编译器只知道它是某种 Number 的子类型。编译器无法确定 list 实际上是 ArrayList<Integer>,所以它也不能保证我们要添加的元素类型是否与集合的实际类型一致。

如果编译器允许我们向 list 中添加元素,可能会导致运行时错误。考虑这种情况:

List<? extends Number> list = new ArrayList<Integer>();
list.add(3.14); // 如果编译器允许这行代码,会导致运行时错误

在这里,list 实际上是一个 ArrayList<Integer> 类型的集合。如果编译器允许我们添加 3.14 这样的 Double 类型元素,这会导致类型不一致的问题,最终导致运行时错误。

阅读数据是安全的

当我们从集合中读取数据时,使用 <? extends T> 是安全的,因为所有的元素至少是 T 类型,因此可以安全地赋值给 T 类型的变量。例如:

List<? extends Number> list = new ArrayList<Integer>();
Number num = list.get(0); // 读取是安全的

在这里,编译器知道集合中的元素是 Number 的子类型,因此可以安全地将元素赋值给 Number 类型的变量。

总结

  • <? extends T> 用于生产者:集合可以是 T 类型或 T 的子类型。这个设计确保从集合中读取数据时是安全的。
  • 不能向集合中添加数据:由于编译器无法确定集合的具体子类型,所以不能添加元素到集合中,防止类型不一致的问题。
  • 读取数据是安全的:因为所有元素都是 T 的子类型,读取操作是安全的,可以保证类型一致性。

通过理解这一点,可以帮助我们编写更加安全和灵活的泛型代码,避免潜在的类型转换问题。


在 Java 泛型中,使用通配符 <? super T> 指定类型的下界,通常用于消费者场景,即只写操作。这种设计可以确保向集合中添加数据是类型安全的,但读取数据时会有一些限制。具体来说,不能假设读取的数据类型,因为集合中可能包含 T 的父类型的对象。

理解 super 用于消费者

当使用 <? super T> 时,表示这个集合可以持有 T 类型及其任何父类型的对象。这种设计适用于将数据添加到集合中的场景(即消费者)。

具体例子
import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<? super Integer> list = new ArrayList<Number>();
        list.add(1); // 可以添加 Integer 类型
        list.add(2);

        // 编译器无法确定读取的数据类型
        // Integer num = list.get(0); // 编译错误
        Object obj = list.get(0); // 可以读取为 Object 类型
    }
}

为什么不能假设读取的数据类型

当我们使用 <? super T> 通配符时,编译器知道集合可以包含 T 类型或 T 的任何父类型的对象。这意味着集合中的元素类型可能比 T 更广泛,可能是 T 的任意父类。因此,编译器不能保证从集合中读取的对象类型是 T

编译器无法确定类型一致性

考虑以下代码:

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<? super Integer> list = new ArrayList<Number>();
        list.add(1); // 可以添加 Integer 类型
        list.add(2);

        // 编译器不知道 list 中的元素确切类型
        // Integer num = list.get(0); // 编译错误
    }
}

在这个例子中,list 的实际类型是 ArrayList<Number>,但编译器只知道它是某种 Integer 的父类型的集合。编译器不能确定 list 实际上是 ArrayList<Number>,所以它也不能保证从 list 中读取的元素类型是 Integer

如果编译器允许我们将读取的数据赋值给 Integer,可能会导致类型转换错误。考虑这种情况:

List<? super Integer> list = new ArrayList<Number>();
list.add(1); // 可以添加 Integer 类型
list.add(2);

// 假设编译器允许如下操作:
Integer num = list.get(0); // 如果编译器允许这行代码,可能导致 ClassCastException

在这里,list 实际上是一个 ArrayList<Number> 类型的集合。如果编译器允许我们将 list.get(0) 的返回值赋值给 Integer 类型的变量,而实际存储的是 Number 类型的对象(比如 Double),则会在运行时抛出 ClassCastException

阅读数据的限制

因为集合中可以包含 T 的任意父类型,编译器只能确保从集合中读取的对象是 Object 类型(因为 Object 是所有类的父类)。这意味着我们不能直接将读取的数据赋值给 T 类型的变量。

示例代码
import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<? super Integer> list = new ArrayList<Number>();
        list.add(1); // 可以添加 Integer 类型
        list.add(2);

        // 读取数据时只能确保是 Object 类型
        Object obj = list.get(0);
        System.out.println(obj);
    }
}

总结

  • <? super T> 用于消费者:表示集合可以持有 T 类型及其父类型的对象。这个设计确保向集合中添加 T 类型的对象是安全的。
  • 读取数据的限制:由于集合中可以包含 T 的父类型的对象,编译器无法确定从集合中读取的数据类型。因此,读取操作只能返回 Object 类型,不能假设读取的数据类型是 T
  • 类型安全:使用 super 可以保证添加操作的类型安全,但读取操作的类型不确定性需要通过类型检查和转换来处理。

通过理解这一点,可以帮助我们编写更加灵活和类型安全的泛型代码,特别是在处理需要添加和读取数据的集合时。

标签:PECS,Java,List,ArrayList,list,类型,编译器,泛型,集合
From: https://blog.csdn.net/weixin_43844521/article/details/140966054

相关文章

  • Day19--Java多线程编程入门学习
    1.什么是多线程?多线程是一种并发编程技术,它允许程序同时执行多个线程。线程是程序执行的基本单位,一个程序至少有一个线程,即主线程。通过使用多线程,可以在一个程序中同时处理多个任务,提高程序的效率和响应能力。2.为什么要使用多线程?提升性能:在多核处理器上,多线程可以将......
  • JavaEE 第3节 线程安全知识铺垫2
    Java中线程终止的方式下面代码我们创建了一个thread线程,如何在main线程(main方法中)终止thread线程?1、手动标记publicclassThreads{publicstaticvoidmain(String[]args){Threadthread=newThread(()->{while(true){......
  • Java基础6
    类与对象类:具有相同特征的事物的抽象描述,是抽象的、概念上的定义。对象:实际存在的该类事物的每个个体,是具体的,因而也称为实例。 面向过程编程(POP) vs   面向对象编程(OOP)面向过程:以“函数”为组织单位,是一种“执行者思维”,适合解决简单问题。拓展能力差、后期维护难度......
  • java学习一周小知识
    java初学习appletJavaApplet可以大大提高Web页面的交互能力和动态执行能力。包含Applet的网页被称为Java-powered页,可以称其为Java支持的网页。当Applet用户访问这样的网页时,Applet被下载到用户的计算机上执行,但前提是用户使用的是支持Java的网络浏览器。由于Applet是在用户的......
  • JavaScript (二十六)——JavaScript 代码规范
    目录JavaScript代码规范变量名空格与运算符代码缩进语句规则对象规则每行代码字符小于80命名规则HTML载入外部JavaScript文件文件扩展名所有的JavaScript项目适用同一种规范。JavaScript代码规范代码规范通常包括以下几个方面:变量和函数的命名规则......
  • JavaWeb中的Tomcat,Servlet详解
    JavaWebJavaWeb技术主要包括服务器技术(后端),如Tomcat,Servlet,JSP等待,以及客户端技术(前端)如HTML,CSS,JavaScript等等Web服务器Web服务器主要负责处理客户端发出的HTTP请求,并做出相应回应Web服务器:安装了服务器软件的计算机,只用于复杂处理请求,发出相应Web服务器......
  • (javaweb)Http协议
    目录一.http概述二.Http-请求协议三.Http响应协议四.Http协议解析一.http概述---复制这个位置会自动前面有http浏览器给服务器发送请求携带请求数据,服务器解析数据,服务器前提需要知道具体格式等等--约定服务器处理完请求,需要给客户端一个响应。返回给客户端浏览器......
  • Javascript——NaN有什么用法
    简介在JavaScript中,NaN(NotaNumber)是一个特殊的值,用来表示非数字的结果,例如一个不合法的数学运算的结果。根据IEEE754浮点数标准,NaN不等于任何值,包括它自己。这意味着NaN是唯一一个与自身不相等的值,所以表达式NaN===NaN返回false。判断NaN如果你需要检测一......
  • Java中的应用监控与日志分析工具选型:从Prometheus到ELK Stack
    Java中的应用监控与日志分析工具选型:从Prometheus到ELKStack大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!在现代软件开发中,应用监控和日志分析是确保系统稳定性和性能的重要组成部分。本文将探讨在Java项目中使用的监控和日志分析工具,重点介绍P......
  • java解一些算法题
    题目描述某部门计划通过结队编程来进行项目开发,已知该部门有N名员工,每个员工有独一无二的职级,每三个员工形成一个小组进行结队编程。结队分组规则如下:从部门中选出序号分别为i、j、k的3名员工,他们的职级分别为level[i],level[j],level[k]结队小组需满足level[i]<le......