Effective Java笔记(29)优先考虑泛型

《Effective Java》第30条:优先考虑泛型方法 原文P106:为了修正这些警告,使方法变成是类型安全的,要将方法声明修改为声明一个类型参数,表示这三个集合的元素类型(两个参数和一个返回值),并在方法中使用类型参数。这里解释下为什么第10行的警告可以被忽略,是因为恒等式返回的就是原本的参数,未做任何类型的修改,所以无论T是什么,返回的都还是T,能保证UnaryOperator 是类型安全的。这里又给出了一个概念:递归类型限制,什么是递归类型限制:类型参数的限制条件里,包含了类型参数本身(像递归一样自我引用)。同学们也可以参照书上的代码测试下。 阅读详情

        一般来说 ,将集合声 明参数化,以及使用 JDK 所提供的泛型方法,这些都不太困难 。编写自己的泛型会比较困难一些,但是值得花些时间去学习如何编写 。

        以简单的(玩具)堆校实现为例 :

// Object -based collection - a prime candidate for generics
public class Stack
{
    private Object[] elements;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;

    public Stack() {
        elements = new Object[DEFAULT_INITIAL_CAPACITY];
    }

    public void push(0bject e) {
        ensureCapacity();
        elements[size++] = e;
    }

    public object pop() {
        if (size == 0)
            throw new EmptyStackException();
        Object result = elements[--size];
        elements[size] = null; // Eliminate obsolete reference
        return result;
    }
    public boolean isEmpty () {
        return size == 0;
    }
    private void ensureCapacity () {
        if (elements.length == size)
            elements = Arrays.copyOf(elements, 2 * size + 1);
    }
}

        这个类应该先被参数化,但是它没有,我们可以在后面将它泛型化( generify ) 。 换句话说,可以将它参数化,而又不破坏原来非参数化版本的客户端代码 。 也就是说,客户端必须转换从堆楼里弹出的对象,以及可能在运行时失败的那些转换 。 将类泛型化的第一步是在它的声明中添加一个或者多个类型参数 。 在这个例子中有一个类型参数,它表示堆桔的元素类型,这个参数的名称通常为 E 。

        下一步是用相应的类型参数替换所有的 Object 类型,然后试着编译最终的程序:

public class Stack<E> {
    private E[] elements;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;
    public Stack() {
        elements = new E[DEFAULT_INITIAL_CAPACITY] ;
    }
    public void push(E e) {
        ensureCapacity();
        elements[size++] = e;
    }
    public E pop( {
        if (size == 0)
            throw new EmptyStackException();
        E result = elements[--size];
        elements[size] = null; // Eliminate obsolete reference
        return result;
    }
    ...// no changes in isEmpty or ensureCapacity
}

        通常,你将至少得到 一个错误提示或警告,这个类也不例外 。 幸运的是,这个类只产生一个错误,内容如下:

        你不能创建不可具体化的( non-reifiable )类型的数组,如 E 。 每当编写用数组支持的泛型时,都会出现这个问题 。 解决这个问题有两种方法 。 第一种,直接绕过创建泛型数组的禁令 : 创建一个 Object 的数组,并将它转换成泛型数组类型 。 现在错误是消除了,但是编译器会产生一条警告 。 这种用法是合法的,但(整体上而言)不是类型安全的:

        编译器不可能证明你的程序是类型安全的,但是你可以 。 你自己必须确保未受检的转换不会危及程序的类型安全性 。 相关的数组(即 elements 变量)保存在一个私有的域中,永远不会被返回到客户端,或者传给任何其他方法 。 这个数组中保存的唯一元素,是传给push 方法的那些元素,它们的类型为 E ,因此未受检的转换不会有任何危害 。 

        一旦你证明了未受检的转换是安全的,就要在尽可能小的 范围中禁止警告 。 在这种情况下,构造器只包含未受检的数组创建,因此可以在整个构造器中禁止这条警告 。 通过增加一条注解   @SuppressWarnings 来完成禁止,Stack 能够正确无误地进行编译,你就可以使用它了,无须显式的转换,也无须担心会出现 ClassCastException 异常:

@SuppressWarnings ("unchecked")
public Stack() {
    elements = (E[]) new Object [DEFAULT_INITIAL_CAPACITY];
}

         消除 Stack 中泛型数组创建错误的第 二种方法是,将 elements 域的类型从 E []改为 Object[] 。 这么做会得到一条不同的错误:

         通过把从数组中获取到的元素由 Object 转换成 E ,可以将这条错误变成一条警告:

        由于 E 是一个不可具体化的( non -reifiable )类型,编译器无法在运行时检验转换 。 你还是可以自己证实未受检的转换是安全的,因此可以禁止该警告 。 我们只要在包含未受检转换的任务上禁止警告,而不是在整个 pop 方法上禁止就可以了,方法如下:

public E pop() {
    if(size==0)
        throw new EmptyStackException();
    // push requires elements to be of type E, so cast is correct
    @SuppressWarnings ("unchecked") E result =
        (E) elements[--size];
    elements[size] = null; // Eliminate obsolete reference
    return result;
}

        这两种消除泛型数组创建的方法,各有所长 。 第一种方法的可读性更强 : 数组被声明为 E []类型清楚地表明它只包含 E 实例 。 它也更加简洁 : 在一个典型的泛型类 中,可以 在代码中的多个地方读取到该数组;第一种方法只需要转换一次(创建数组的时候),而第二种方法则是每次读取一个数组元素时都需要转换一次 。 因此,第一种方法优先,在实践中也更常用 。 但是,它会导致堆污染( heap pollution ),详见第 32 条 : 数组的运行时类型与它 的编译时类型不匹配(除非 E 正好是 Object ) 。 这使得有些程序员会觉得很不舒服,因而选择第二种方案,虽然堆污染在这种情况下并没有什么危害 。

        下面的程序示范了泛型 Stack 类的使用方法 。 程序以倒序的方式打印出它的命令行参数,并转换成大写字母 。 如果要在从堆战中弹出的元素上调用 String 的 toUpperCase方法,并不需要显式的转换,并且确保自动生成的转换会成功:

public static void main(String[] args) {
    Stack<String> stack = new Stack<>();
    for (String arg : args)
        stack.push(arg);
    while (!stack.isEmpty())
        System.out.println(stack.pop().toUpperCase();
}

        看来上述的示例与第 28 条相矛盾了,第 28 条鼓励优先使用列表而非数组 。 实际上不可能总是或者总想在泛型中使用列表 。Java 并不是生来就支持列表,因此有些泛型如 ArrayList,必须在数组上实现 。 为了提升性能,其他泛型如 HashMap 也在数组上实现 。

        总而言之,使用泛型比使用需要在客户端代码中进行转换的类型来得更加安全,也更加容易。 在设计新类型的时候,要确保它们不需要这种转换就可以使用 。 这通常意味着要把类做成是泛型的。 只要时间允许,就把现有的类型都泛型化 。 这对于这些类型的新用户来说会变得更加轻松,又不会破坏现有的客户端 。

《Effective Java》第29条:优先考虑泛型 这种方式会消除错误,但是会得到一条非受检的警告(详见27条),我们自己能确定该代码是安全的,且强转不回会危及程序的安全,因为 elements 数组是一个private的,且没有提供访问该数组的方法,该数组获取的值都来自于push方法,该方法的参数类型为 E,所以该强转不会有危害。原文P106:绝大多数泛型就像我们的 Stack 示例一样,因为它们的类型参数没有限制:你可以创建 Stack、Stack、Stack>,或者任何其他对象弓用类型的 Stack。只要时间允许,就把现有的类型都泛型化。 阅读详情

相关推荐

Effective Java 泛型 第29条:优先考虑类型安全的异构容器

泛型最常用于集合,如 Set 和 Map ,以及单元素的容器。 如 ThreadLocal 和 AtomicReference 。在这些用法中,它都充当被参数化了的容器。这样就限制你每个容器只能有固定数目的类型参数。一般来说,这种情况正是你想要的。一个 Set 只有一个类型参数,表示它的元素类型;一个 Map 有两个类型参数,表示它的键和值类型;诸如此类。但是,有时候你会需要更多的灵活性。例如,数...

yancyang的博客 3066

学习记录:Effective Java-优先考虑泛型

在Java中,泛型是一种强大的特性,可以提高代码的类型安全性和可读性。因此,在编写代码时应该优先考虑使用泛型。

weixin_36639192的博客 533

【论文分享】【Agent】ProAgent: Building Proactive Cooperative Agents with Large Language Models

多智能体系统是由多个智能体组成的集合,这些智能体可以是软件程序、机器人、传感器或其他实体,它们各自具备一定的智能和自主性,并通过交互协作来实现单个智能体难以完成的复杂目标。

nanxun___的博客 1122

建议:优先考虑泛型方法。

就如类可以从泛型中受益一般,方法也一样。静态工具方法尤其适合于泛型化。Collections中的所有“算法”方法(例如binarySearch和sort)都泛型化了。 编写泛型方法与编写泛型类型相类似。例如下面这个方法,它返回两个集合的联合: // Use raw types - unacceptable! (Item 23) public static ...

孤芳不自赏 1999

【QT】QtCreator卸载与安装(非正常状态)

QtCreator卸载与安装(非正常状态)

Cappuccino Blog 1万+

半桥LLC谐振变换器及同步整流MATLAB仿真(二)

接上文,半桥LLC谐振变换器及同步整流MATLAB仿真

qq_55203246的博客 4330

第二十六条:优先考虑泛型

一、利用Object写的堆栈实例 public class CommonStack { private static final int DEFAULT_SIZE = 16; //用来存储的数组 private Object[] objects = new Object[DEFAULT_SIZE]; //当前存储的位置 private int count = 0; public

chen19960724的博客 421

GEE开发之ERA5(气温、降水、压力、风速等)数据获取和分析

前言:ERA5主要提供DAILY和MONTHLY两大类别数据,其中主要包括气温、降水、压力、风速等。

等待着冬天的风的博客 3万+

【笔记29】优先考虑泛型

// Initial attempt to generify Stack = won't compile! public class Stack<E> { private E[] elements; private int size = 0; private static final int DEFAULT_INITIAL_CAPACITY = 16; public St...

u013773608的博客 249

CMM软件:PC-DMIS汽车制造二次开发

PC-DMIS 是一种广泛应用于坐标测量机(CMM)的测量软件,它不仅提供了强大的测量功能,还支持用户通过二次开发来扩展其功能。二次开发可以显著提升测量效率,满足特定的测量需求,减少重复性工作,提高数据处理和分析的准确性。本节将介绍 PC-DMIS 二次开发的基本概念、开发环境的搭建以及开发中常用的工具和方法。PC-DMIS 二次开发为用户提供了强大的工具,可以显著提升测量效率、减少重复性工作并提高数据处理和分析的准确性。

kkchenjj的博客 1814

导数与微分在 FPS 射击游戏中的实际应用:把“手感”算出来

摘要:射击游戏开发中,单线程适用于简单小游戏(如单机打靶),所有任务由主线程顺序处理,代码简单但效率较低;多线程则适合复杂场景(如多人在线3D大作),通过渲染、逻辑、网络、音效等线程分工协作,确保画面流畅、响应迅速和实时同步。选择依据取决于游戏复杂度、联网需求和性能要求,小型游戏用单线程,大型高要求项目必须采用多线程架构。(149字)

本博客聚焦游戏开发技巧、创业实战心得及大学热门课程干货。这里既有技术深度,也有思维广度,无论你是开发者、创业者还是高校学子,都能在这里找到适合自己的成长之路! 3278

第27条 泛型——优先考虑泛型方法

术语:  递归类型限制:通过某个包含该类型参数本身的表达式来限制类型参数。         考虑如下的方法,它的作用是返回两个集合的联合: [java] view plain copy // Users raw types - unaccepable!   public static Set union(Set s1,

Xi__Yuan的专栏 2751

Unity零基础到入门 ☀️| 【UI系统学习】轻松学会 Unity 的 UGUI基础控件的使用(包括一个简易血条、蓝条设计)

Unity,UGUI。本篇博客来挑几种Unity中最常用的UI控件如何使用和使用技巧,包括一个简易血条蓝条的小实例

努力前行,总会成为自己心中的那道光 2万+

Effective Java学习(泛型)之——优先考虑泛型化方法

       就如类可以从泛型中受益一般,方法也是一样。静态工具方法尤其适合于泛型化。Collections中的所有的“算法”方法,例如(binarySearch和sort)都泛型化了。   编写泛型化的方法与编写泛型化的类相似。例如下面这个方法,他返回两个集合的联合:   public static Set union(Set set1,Set set2){ Set resul...

sungang1120的专栏 330

【DeepSeek R1构建本地RAG知识库】数据结构化清洗-知识图谱制作、使用、dify集成

本文探讨了在构建本地RAG(Retrieval-Augmented Generation)系统时,如何通过引入知识图谱来增强系统的知识理解能力。传统的向量数据库和嵌入模型虽然能处理语义匹配,但无法有效捕捉知识间的关联性。知识图谱通过“实体-关系-属性”的结构化表示,能够更好地支持推理、推荐和多跳问答等高级功能。文章详细介绍了知识图谱的基本概念、制作思路,以及如何使用Neo4j图数据库进行知识图谱的构建和查询。通过实例展示了从PDF文件解析到知识图谱入库的全过程,强调了知识图谱在提升RAG系统性能中的重要作用

少说,多做 564

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

《Effective Java》学习笔记——第5部分 泛型的使用

Effective Java》共有多个章节,每个章节提供了若干条建议或最佳实践,整理了对应章节关键的实践内容,用于自己后续代码开发过程中,参考对照,自查代码习惯,从而提升输出的代码质量。《Effective Java》第5部分“泛型”主要讲如何在 Java 中正确有效地使用泛型。泛型是 Java 5 引入的重要特性,它增强了类型安全性、减少了类型转换的需求,并且使得代码更加通用和灵活。但是,泛型的使用不当也可能导致程序的错误或性能问题。掌握泛型的使用技巧和最佳实践,对于提升个人代码能力非常重要。

weixin_37693760的博客 812

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

Effective Java 泛型 第26条:优先考虑泛型

一般来说,将集合声明参数化,以及使用JDK所提供的泛型和泛型方法,这些都不太困难,编写自己的泛型会比较困难一些,但是值得花些时间去学习如何编写。 考虑第6条中这个简单的堆栈实现: // Object -based Collection - a prime candidate for generics public class Stack{ private Object[] elem...

yancyang的博客 6264

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

Effective Java学习笔记--第29、30条优先考虑泛型和泛型方法

这两条主要讲述了泛型对于类与方法的构建能起到很多积极的作用。

weixin_50353846的博客 960

《Effective Java》泛型

《Effective Java》第四章 泛型

qq_43350524的博客 682

Effective Java 泛型 第27条:优先考虑泛型方法

就如类可以从泛型中受益一般,方法也一样。静态工具方法尤其适合于泛型化。 编写泛型方法与编写类型类型相类似。 例:他返回两个集合的联合: // Users raw types - unaccepable! public static Set union(Set s1, Set s2) { Set result = new HashSet(s1); result.addAll...

yancyang的博客 6263

优先考虑泛型:Effective Java第29条深度解析与工程实践

类型安全是Java编程的核心诉求之一,泛型机制通过在编译期进行类型检查,显著降低运行时ClassCastException风险。泛型采用类型擦除实现,而数组在运行时保留类型信息,二者冲突导致泛型数组创建受限,由此引出类型擦除与协变原理的讨论。优先设计泛型类与泛型方法,能大幅提升API的类型安全与可读性,是构建基础组件的重要实践。从集合容器、缓存组件到配置中心,泛型化改造广泛应用于真实工程场景。围绕《Effective Java》第29条“优先考虑泛型”,深入拆解Object堆栈的泛型化全过程,剖析泛型数组、

weixin_28683689的博客 269

Java:Effective java学习笔记之 优先考虑泛型和泛型方法

Java优先考虑泛型和泛型方法优先考虑泛型参考 优先考虑泛型 参考 1、建议:优先考虑泛型方法。 2、Effective Java 优先考虑泛型 3、Effective Java笔记第四章泛型第四节优先考虑泛型

JMW1407的博客 545

Effective Java 学习笔记之泛型

1.不要在新代码中使用原生态类型1) 声明中具有一个或者多个类型参数的类或者接口就是泛型类或接口 例如List<E>(读作‘E的列表’) 泛型类和接口统称作泛型2)每个泛型都定义一个原生态类型,即不带任何实际类型参数的泛型名称。例如List<E>相对应的原生态类型是List3)使用原生态类型,如果放入的类型不一致,在编译和运行时不会报错,直到从其中取出元素的时候才会收到提示 List

Heqianqian的博客 1112

effective Java-泛型

泛型 26. 请不要使用原生态类型 泛型类和泛型接口统称为泛型generic 每一种泛型定义一组参数化的类型parameterized type 每一种泛型都定义一个原生态类型raw type:即不带任何实际类型参数的泛型名称,如List原生类型是List,主要为了泛型出现之前的代码进行兼容。 如果使用原生态类型,就失掉了泛型在安全性和描述性方面的所有优势 原生态类型List和参数化的类型List...

white_while的博客 425

Effective Java之泛型 总结

第23条 请不要在新代码中使用原生态类型 如果使用原生态类型 就失掉了泛型在安全性和表述性方面的所有优势 在类文字(class literal)中必须使用原生态类型 分类 术语 示例 参数化的类型 List&amp;lt;String&amp;gt; 实际类型参数 String 泛型 List&amp;lt;E&amp;gt; 形式类型参数 E 无限制通配符类型 List&a

勤劳的搬运工 285

《Effective Java》解读第29条:优先考虑泛型

Java泛型是增强代码类型安全性和可读性的核心工具。不使用泛型的代码会产生大量运行时强制转换,既不安全也容易出错。

Cosmoshhhyyy的博客 477
上一篇: Effective Java笔记(28)列表优于数组
下一篇: Effective Java笔记(30)优先考虑泛型方法
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值