Effective Java -- 对于所有对象都通用的方法 -- 覆盖equals时请遵守通用约定

本文深入探讨了在Java中覆盖equals方法的必要性和正确做法,解析了自反性、对称性、传递性、一致性和非空性的通用约定,提供了实用的代码示例和覆盖技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第三章 对于所有对象都通用的方法

本章将讲述何时以及如何覆盖Object类中的非final方法(equals、hashCode、toString、clone和finalize)。

第十条 覆盖equals时请遵守通用约定

覆盖equals方法看起来很简单,但是会许多覆盖方式会导致错误。

一 有时并不需要覆盖

有时,我们期望类的每个实例都只与它自身相等:

1 类的每个实例本质上都是唯一的

对于代表活动实体而不是值的类(如Thread)正是如此。

2 类没有必要提供“逻辑相等”的测试功能

设计者并不认为客户需要这样的功能(如Pattern)

3 父类已经覆盖了equals

并且父类的行为对于这个类也是合适的(如Set从Abstract继承equals实现)

4 类是私有的或包级私有的

这个类的equals方法永远不会被调用。
但是如果非常想规避风险,也可以选择覆盖equals方法,以确保它不会被意外调用:

	@Override
	public boolean equals(Object o) {
		throw new AssertionError();
	}
5 单例模式的类

对于singleton而言,逻辑相同和对象等同是一回事。

二 通用约定

当类具有自己特有的“逻辑相等”概念时,可以选择覆盖equals方法,这通常属于“值类”的情形(如Integer或者String)。

在覆盖equals方法时,必须遵守它的通用约定:

1 自反性(reflexive)
 对于任何非 null 的引用值 x ,
 x.equals(x) 必须返回 true 。

即对象必须等于其本身。

2 对称性(symmetric)
 对于任何非 null 的引用值 x 和 y,
 当且仅当 y.equals(x) 返回 true 时,
 x.equals(y) 必须返回 true 。

任何两个对象对于“它们是否相等”的问题都必须保持一致。

3 传递性(transitive)
  对于任何非 null 的引用值 x 、 y 和 z,
  如果 x.equals(y) 返回 true,
  并且 y.equals(z) 也返回 true 时,
  x.equals(z) 必须返回 true 。

如果第一个对象等于第二个对象,而第二个对象又等于第三个对象,则第一个对象一定等于第三个对象。

4 一致性(consistent)
 对于任何非 null 的引用值 x 和 y,
 只要 equals 的比较操作在对象中所用的信息没有被修改,
 多次调用应该一致的返回相同结果

如果两个对象相等,它们就必须始终保持相等,除非它们中有一个对象被修改了。

5 非空性(Non-nullity)
 对于任何非 null 的引用值 x,x.equals(null) 必须返回 false。

所有对象都不能等于 null。

@Override
public boolean equals(Object o) {
	if (! (o instanceof MyType)){
		return false;
	}
	MyType mt = (MyType) o;
	...
}

三 诀窍

1 使用 ==

使用 == 操作符检查“参数是否为这个对象的引用”。如果是,则返回 true。

2 使用 instanceof

使用 instanceof 操作符检查“参数是否为正确的类型”。如果不是,则返回false。

3 把参数转换成正确的类型
4 检查每个“关键”域

对于该类中的每个“关键”域,检查参数中的域是否与该对象中对应的域相匹配。

四 例子

public final class PhoneNumber {
    private final short areaCode, prefix, lineNum;

    public PhoneNumber(int areaCode, int prefix, int lineNum) {
        this.areaCode = rangeCheck(areaCode, 999, "area code");
        this.prefix = rangeCheck(prefix, 999, "prefix");
        this.lineNum = rangeCheck(lineNum, 9999, "line num");
    }

    //检查范围
    private static short rangeCheck(int val, int max, String arg) {
        if(val < 0 || val > max) {
            throw new IllegalArgumentException(arg + ":" + val);
        }
        return (short)val;
    }

    @Override
    public boolean equals(Object o) {
        if(o == this) {
            return true;
        }
        if(!(o instanceof PhoneNumber)) {
            return false;
        }
        PhoneNumber pn = (PhoneNumber)o;
        return pn.lineNum == this.lineNum && pn.prefix == this.prefix && pn.areaCode == this.areaCode;
    }
}

编写和测试equals方法都是十分繁琐的,最佳途径是使用 Google 开源的 AutoValue 框架,它会自动生成这些方法,通过类中的单个注解就能触发。

五 总结

不要轻易覆盖 equals 方法,除非迫不得已。
如果覆盖 equals 方法,一定要比较所有关键域,并查看它们是否遵守合约的五个条款。

### LlamaIndex 多模态 RAG 实现 LlamaIndex 支持多种数据类型的接入与处理,这使得它成为构建多模态检索增强生成(RAG)系统的理想选择[^1]。为了实现这一目标,LlamaIndex 结合了不同种类的数据连接器、索引机制以及强大的查询引擎。 #### 数据连接器支持多样化输入源 对于多模态数据的支持始于数据收集阶段。LlamaIndex 的数据连接器可以从多个异构资源中提取信息,包括但不限于APIs、PDF文档、SQL数据库等。这意味着无论是文本还是多媒体文件中的内容都可以被纳入到后续的分析流程之中。 #### 统一化的中间表示形式 一旦获取到了原始资料之后,下一步就是创建统一而高效的内部表达方式——即所谓的“中间表示”。这种转换不仅简化了下游任务的操作难度,同时也提高了整个系统的性能表现。尤其当面对复杂场景下的混合型数据集时,良好的设计尤为关键。 #### 查询引擎助力跨媒体理解能力 借助于内置的强大搜索引擎组件,用户可以通过自然语言提问的形式轻松获得所需答案;而对于更复杂的交互需求,则提供了专门定制版聊天机器人服务作为补充选项之一。更重要的是,在这里实现了真正的语义级关联匹配逻辑,从而让计算机具备了一定程度上的‘认知’功能去理解和回应人类意图背后所蕴含的意义所在。 #### 应用实例展示 考虑到实际应用场景的需求多样性,下面给出一段Python代码示例来说明如何利用LlamaIndex搭建一个多模态RAG系统: ```python from llama_index import GPTSimpleVectorIndex, SimpleDirectoryReader, LLMPredictor, PromptHelper, ServiceContext from langchain.llms.base import BaseLLM import os def create_multi_modal_rag_system(): documents = SimpleDirectoryReader(input_dir='./data').load_data() llm_predictor = LLMPredictor(llm=BaseLLM()) # 假设已经定义好了具体的大型预训练模型 service_context = ServiceContext.from_defaults( chunk_size_limit=None, prompt_helper=PromptHelper(max_input_size=-1), llm_predictor=llm_predictor ) index = GPTSimpleVectorIndex(documents, service_context=service_context) query_engine = index.as_query_engine(similarity_top_k=2) response = query_engine.query("请描述一下图片里的人物表情特征") print(response) ``` 此段脚本展示了从加载本地目录下各类格式文件开始直到最终完成一次基于相似度排序后的top-k条目返回全过程。值得注意的是,“query”方法接收字符串参数代表使用者想要询问的内容,而在后台则会自动调用相应的解析模块并结合先前准备好的知识库来进行推理计算得出结论。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值