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

为何Opaque Type的内联方法可在具体类调用却无法在特质中访问?

Scala Opaque Type内联方法在特质中无法访问的问题解决

问题场景

定义基于BigDecimal的Opaque Type后,给顶层Opaque Type添加了内联扩展方法。在具体类中调用该方法完全正常,但在包含抽象类型成员的特质里调用时,编译器报错无法访问该方法。

复现代码

opaque type ConstrainedBigDecimal = BigDecimal
extension (n: ConstrainedBigDecimal)
    inline def isWhole = n.isWhole
opaque type PositiveBigDecimal <: ConstrainedBigDecimal = BigDecimal
opaque type NegativeBigDecimal <: ConstrainedBigDecimal = BigDecimal
sealed trait Quantity:
    protected type QuantityType <: ConstrainedBigDecimal
    def quantity: QuantityType
    def formatQuantity: String =
        if quantity.isWhole then quantity.toString() else f"$quantity%.2f"

报错信息

-- Error: test.scala:15:20 --------------------------------------------------------------------------------------------------------------------------
15 |        if quantity.isWhole then quantity.toString() else f"$quantity%.2f"
   |           ^^^^^^^^^^^^^^^^
   |           isWhole cannot be accessed as a member of (n$proxy1 : Quantity.this.QuantityType) from trait Quantity.
   |-------------------------------------------------------------------------------------------------------------------------------------------------
   |Inline stack trace
   |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   |This location contains code that was inlined from test.scala:4
 4 |    inline def isWhole = n.isWhole
   |                         ^^^^^^^^^
    -------------------------------------------------------------------------------------------------------------------------------------------------

对比:正常编译的具体类

case class PositiveQuantity(
    quantity: PositiveBigDecimal
):
    def formatQuantity: String =
        if quantity.isWhole then quantity.toString() else f"$quantity%.2f"

问题原因

内联方法的解析发生在编译期,特质中的QuantityType是抽象类型,编译器在编译特质本身时,无法确定它具体对应哪个Opaque Type。Opaque Type的透明性仅在其定义作用域内有效,对于泛化的抽象类型,编译器无法在编译特质阶段完成内联扩展的展开,因此报错找不到isWhole方法。而具体类中quantity的类型是明确的PositiveBigDecimal,编译器能直接解析内联扩展并完成展开。

解决方案

方案1:移除扩展方法的inline修饰符

将内联扩展方法改为普通扩展方法,编译器会通过隐式扩展机制处理,不再依赖编译期的类型确定:

opaque type ConstrainedBigDecimal = BigDecimal
extension (n: ConstrainedBigDecimal)
    def isWhole = n.isWhole // 去掉inline
opaque type PositiveBigDecimal <: ConstrainedBigDecimal = BigDecimal
opaque type NegativeBigDecimal <: ConstrainedBigDecimal = BigDecimal
sealed trait Quantity:
    protected type QuantityType <: ConstrainedBigDecimal
    def quantity: QuantityType
    def formatQuantity: String =
        if quantity.isWhole then quantity.toString() else f"$quantity%.2f"

方案2:将特质中的方法改为inline

保持扩展方法的inline,同时把特质里的formatQuantity改成inline def,这样内联展开会延迟到具体实现类的编译阶段,此时QuantityType的具体类型已明确,编译器能正常解析:

opaque type ConstrainedBigDecimal = BigDecimal
extension (n: ConstrainedBigDecimal)
    inline def isWhole = n.isWhole
opaque type PositiveBigDecimal <: ConstrainedBigDecimal = BigDecimal
opaque type NegativeBigDecimal <: ConstrainedBigDecimal = BigDecimal
sealed trait Quantity:
    protected type QuantityType <: ConstrainedBigDecimal
    def quantity: QuantityType
    inline def formatQuantity: String = // 改为inline方法
        if quantity.isWhole then quantity.toString() else f"$quantity%.2f"

// 具体实现类可正常编译
case class PositiveQuantity(quantity: PositiveBigDecimal) extends Quantity

内容的提问来源于stack exchange,提问作者Nil Admirari

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 21:10:32