【问题标题】:Why is "abstract override" required not "override" alone in subtrait?为什么在子特征中不需要“抽象覆盖”而不是单独“覆盖”?
【发布时间】:2019-11-16 16:35:22
【问题描述】:

我阅读了 section 的 Programming in Scala,其中介绍了 abstract override,但我仍然对这些修饰符的加入究竟意味着什么感到困惑。使用这些修饰符的代码的 sn-p 粘贴在下面:

trait Doubling extends IntQueue {
    abstract override def put(x: Int) { super.put(2 * x) }
}

特别是,我很困惑abstract 在这种情况下的用途,以及为什么我们不能简单地使用override 关键字来达到预期的结果。如果我们不包括对super 的调用,我们是否需要关键字abstract?为什么或者为什么不?我正在寻找这个关键字组合的详细解释,因为它与可堆叠特征有关。

【问题讨论】:

  • spec says: "请注意,抽象覆盖修饰符组合不会影响成员是具体的还是抽象的概念。如果只给它一个声明,它就是抽象的;它是具体的如果给出了完整的定义。”抽象abstract override 值成员的用例是什么?它当然不是指超级,它没有初始化程序(或主体,如果是方法)。它与只是一个抽象的覆盖成员有什么不同,省略了abstract

标签: scala traits


【解决方案1】:

原因是基类方法是抽象

abstract class IntQueue {
  def get(): Int
  def put(x: Int)
}

如果您不将 abstract 放在特征上,您最终会得到您正在寻求的解释:

trait Doubling extends IntQueue {
     override def put(x: Int) { super.put(2 * x) }
}
<console>:9: error: method put in class IntQueue is accessed from
 super. It may not be abstract unless it is overridden by a member 
 declared `abstract' and `override'
            override def put(x: Int) { super.put(2 * x) }

所以 - 您需要将该方法标记为 abstract

这是等式的“另一面”:如果方法确实有实现,则无需将trait 的方法标记为abstract

 abstract class IntQueue {
    import collection.mutable._
        val q  =  Queue[Int]()
      def get(): Int = { q.dequeue() }
      def put(x: Int) = { q.enqueue(x) }
   }

现在不需要包含abstract

 trait Doubling extends IntQueue {
        /* Look Ma! no abstract here ! */   override def put(x: Int) { super.put(2 * x) }
      }
defined trait Doubling

【讨论】:

  • 谢谢。出于某种原因,我不断看到abstract override 的可堆叠特征示例,但在我看来这两个概念实际上是分开的(尽管两者都依赖于动态绑定super)。
【解决方案2】:

这个想法是它是一个不完整的覆盖——即使您正在修改该假设方法的行为,您仍然希望要求该特征的最终具体实现来提供该方法。换句话说,您要覆盖的方法不是完全独立的实现。它提供了与 Python 中的 method decorator 类似的效果。

据我所知,一个 trait 上的方法是 abstract override 当且仅当它调用 super,但它破坏了封装,期望代码的客户端检查方法的实现以了解它需要具体的实现。因此,您必须将其标记为abstract override 才能完全定义接口。

【讨论】:

    【解决方案3】:

    late binding in scala traits 的一部分帖子;提供了非常清晰的解释;以下逐字提供(阅读full post了解更多信息):

    抽象基类提供了requestApproval 方法的实现。这很好,因为最左边的 trait 调用了这个方法。如果基类的方法是抽象的会怎样?

    abstract class ApprovalRequest {
      def requestApproval()
    }
    

    如果我们改变它,我们会从编译器得到一个相当奇怪的消息: 错误:访问类ApprovalRequest 中的方法requestApproval 来自超级。除非被成员覆盖,否则它可能不是抽象的 声明 abstractoverride abstractoverride 的组合告诉编译器该方法的最终实现将由混合特征的类提供。如果我们将 abstract 关键字添加到方法中,我们就不能再使用 ApprovalRequest 的匿名实现。无法创建该对象,因为抽象覆盖方法将寻找 requestApproval 的实现,但没有。相反,我们必须创建一个扩展ApprovalRequest 并实现requestApproval 的新类。然后我们将这些特征混合到该类的一个实例中。

    class ApprovalDelegate extends ApprovalRequest {
      override def requestApproval() {
        println("and now we play the waiting game")
      }
    }
    
    val adCampaign = new ApprovalDelegate with MarketingApprovalRequest
       with FinanceApprovalRequest with ExecutiveApprovalRequest
    

    现在将给出输出:

    requesting approaval from executives
    requesting approval from Finance
    requesting approval from Marketing
    and now we play the waiting game
    

    【讨论】:

      猜你喜欢
      • 2011-02-28
      • 1970-01-01
      • 2017-08-28
      • 1970-01-01
      • 2011-01-03
      • 1970-01-01
      • 2012-12-25
      • 1970-01-01
      • 2022-08-18
      相关资源
      最近更新 更多