You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Scala 3 strictEquality模式下类型参数相等性比较的疑问

Scala 3 strictEquality 模式下的相等性比较问题解答

问题背景

启用-language:strictEquality后,以下代码会触发编译错误:

trait X[A]:
    def val1: A
    def val2: A
    def f() =
        if val1 == val2 then
            println("Same")

错误提示:

Values of types A and A cannot be compared with == or !=

添加以下代码可解决该问题:

given CanEqual[A, A] = CanEqual.derived

疑问解答

1. 为何相同类型的实例无法进行相等性比较?哪些A类型会导致val1 == val2不合法?

在strictEquality模式下,Scala不再默认允许任意类型使用==/!=,而是要求存在隐式的CanEqual[L, R]实例,以此证明左右类型的相等比较是安全且符合预期的。

对于类型参数A,编译器无法提前确定它的具体类型:

  • 如果A是未定义明确相等语义的抽象类型(比如未实现equals的特质),直接比较可能得到非预期的结果;
  • 如果A是Scala默认不提供CanEqual实例的类型(比如Function1等函数类型,因为函数的引用相等通常不是业务逻辑需要的相等判断);
  • 甚至是自定义的未正确重写equals/hashCode的类,误用==会触发引用相等判断,而非业务期望的内容相等。

因此编译器在严格模式下不会默认给A生成CanEqual[A, A]实例,必须由开发者显式声明。

2. 是否存在可给类型添加==、!=操作的特质?

存在,Scala 3提供了scala.Equals特质:

  • 任何类型只要继承Equals,就需要实现equals和hashCode方法,编译器会自动为其生成对应的CanEqual实例,从而支持==/!=操作;
  • 此外,也可以像问题中的解决方案那样,手动为类型提供CanEqual隐式实例,直接开启相等比较能力。

3. 编译器无法推断类型参数A可与自身比较,为何上述given语句能解决问题?CanEqual.derived的作用是什么,为何默认不自动启用?

  • given CanEqual[A, A] = CanEqual.derived是显式为类型参数A提供了一个自身到自身的CanEqual实例,明确告诉编译器:A类型的实例可以安全地互相比较。
  • CanEqual.derived是一个推导方法,它会在当前上下文里自动寻找或生成合适的CanEqual实例:如果A是Equals的子类、基本类型、字符串、标准集合等已有默认CanEqual实现的类型,derived会直接复用这些实例;如果A是自定义类型,也会基于其equals方法生成对应的实例。
  • 之所以默认不自动启用CanEqual.derived,是因为strictEquality的设计目标就是避免不安全的相等比较:Scala 2中默认允许所有类型用==,经常出现开发者误将引用相等当成内容相等的情况(比如函数实例、自定义类未重写equals时)。强制显式声明CanEqual实例,是让开发者明确确认该类型的相等比较逻辑是符合预期的,从根源减少bug。

内容的提问来源于stack exchange,提问作者Victor Grigoriu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 11:25:17