为何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
相关产品推荐
相关产品推荐

