【问题标题】:In Scala, how can an Inner case class consistently override a method?在 Scala 中,Inner case 类如何始终如一地覆盖一个方法?
【发布时间】:2019-04-22 21:36:26
【问题描述】:

我最近发现 Scala 编译器对 case class 有一个有趣的特性:由于它同时生成类和对象签名,如果定义为内部类,它可以用来覆盖抽象类型定义和函数定义它的超类具有最少的样板代码,这里是一个例子:

object InnerCaseClassOverridingBoth {

  trait AALike

  trait SS {
    type AA <: AALike
    def AA(): AnyRef
  }

  trait SS_Clear extends SS {
    def AA(): AnyRef
  }

  class SSA extends SS_Clear {
    case class AA() extends AALike
  }
  object SSA extends SSA {}
}

这将编译没有任何错误。然而快捷方式在这里停止,如果函数定义def AA被参数化,那么内部案例类和内部对象都不能覆盖它:内部对象的apply函数不会自动扩展为它的方法外部类:

  trait SS_Parameterised extends SS {
    def AA(ii: Int): AnyRef
  }

  class SSB extends SS_Parameterised {
    case class AA(ii: Int) extends AALike
  }
  object SSB extends SSB {}

这给出了一个错误:

class SSB needs to be abstract, since method AA in trait
SS_Parameterised of type (ii: Int)AnyRef is not defined
    class SSB extends SS_Parameterised {

我的问题是,在这种情况下是否有捷径?为什么 Scala 编译器设计为链接 case 1 而不是 case 2?

【问题讨论】:

    标签: scala inheritance overriding case-class


    【解决方案1】:

    它根本不是特别设计的;或者,它是,但不是你想的那样。您不是用构造AA 的方法覆盖def AA(),而是用object AA 本身覆盖它。通知

    trait T {
       type I <: AnyRef
       def I(): AnyRef
    }
    object O extends T {
       case class I(val i: Int)
    }
    

    这很好用。

    > (O: T).I()
    I
    > (O: T).I().getClass
    class O$I$
    > O.I(5)
    I(5)
    > O.I(5).getClass
    class O$I
    

    突出的设计选择是“objects 可以覆盖无参数defs”(vals、vars 也可以,当然还有无参数defs)和“case classes 自动生成objects”。 “内部case classes 用它们的构造函数覆盖外部类中的同名方法”不是Scala 的规则之一。 object O 包含一个case class I 和一个object I,抽象def I(): AnyRef 被覆盖以返回object Iobject I 的内容无关紧要,因为def I() 只需返回一个AnyRef,这意味着没有任何限制。这是完全有道理的

    trait U {
       type I <: AnyRef
       def I(i: Int): AnyRef
    }
    object P extends U {
       case class I(i: Int)
    }
    

    然后失败。 object P 包含一个case class I 和一个关联的object I,但它还需要一个def I(i: Int): AnyRef,它缺少。

    【讨论】:

    • 同意,但在这种情况下,无法再定义 def I(i: Int)。这是一个很好的答案,但让我们看看 scala 编译器设计者能否在几天内澄清这一点。
    • @tribbloid “def I(i: Int) 不能再定义”是什么意思?在什么“情况下”?我可以在object Oobject P 中添加def I(i: Int): AnyRef = java.lang.Integer.valueOf(i) 或其他任何内容,然后编译它们。
    • 是的,你是对的,只是做了一个快速的实验,结果证明新的函数定义是可能的。接受为规范答案
    【解决方案2】:

    我猜这只是与apply 在案例类中扮演的角色有关。见Case Class default apply method SSA 通过 SSA (SSA.apply) 的伴随对象满足 SS_Clear.AA

    当您向方法添加参数时,您不再拥有 0 参数 apply 方法来履行该角色。

    【讨论】:

      【解决方案3】:

      好的,我找到了两种方法

      方法一:被案例类覆盖:

        trait SS_Parameterised {
          type AA <: AALike
          def AA: Int => AnyRef
        }
      

      方法2:被隐式类覆盖:

        trait SS_Parameterised {
          type AA <: AALike
          implicit def AA(ii: Int): AnyRef
        }
      
        class SSB extends SS_Parameterised {
          implicit class AA(ii: Int) extends AALike
        }
      

      故事结束 :) 一个案例类覆盖 2 个声明?没问题。

      (方法2作为scala在内部为每个隐式类生成一个隐式函数)

      【讨论】:

        猜你喜欢
        • 2018-08-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-04-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-04-09
        相关资源
        最近更新 更多