【问题标题】:In what scenario does self-type annotation provide behavior not possible with extends在什么情况下,自类型注释提供了扩展无法实现的行为
【发布时间】:2015-03-27 18:29:58
【问题描述】:

我试图提出一个组合场景,其中 self-type 和 extends 行为不同,到目前为止还没有找到。基本示例总是谈论不需要类/特征不必是依赖类型的子类型的自我类型,但即使在这种情况下,自我类型和扩展之间的行为似乎是相同的。

trait Fooable { def X: String }
trait Bar1 { self: Fooable =>
  def Y = X + "-bar"
}
trait Bar2 extends Fooable {
  def Y = X + "-bar"
}
trait Foo extends Fooable {
  def X = "foo"
}
val b1 = new Bar1 with Foo
val b2 = new Bar2 with Foo

在使用一个与另一个时,是否存在某种形式的组合或组合对象的功能不同的情况?

更新 1: 感谢您提供没有自我输入就不可能实现的事情的示例,我很欣赏这些信息,但我真的在寻找可以使用 self 和 extends 的组合,但不是可以互换。

更新 2: 我想我的具体问题是为什么各种 Cake Pattern 示例通常都在谈论必须使用 self-type 而不是 extends。我还没有找到一个蛋糕模式场景不能与扩展一样好用

【问题讨论】:

标签: scala extends self-type cake-pattern


【解决方案1】:

循环引用可以用自类型完成,但不能用扩展:

// Legal
trait A { self: B => }
trait B { self: A => }

// Illegal
trait C extends D
trait D extends C

当存在循环依赖时,我有时会使用它来跨多个文件拆分实现。

【讨论】:

  • 我发现了另一件你可以用 self 类型做而你不能用 extends 做的事情:自我类型可以是结构类型
【解决方案2】:

还有,

scala> trait A { def a: String ; def s = "A" }
defined trait A

scala> trait B { _: A => def s = "B" + a }
defined trait B

scala> trait C extends A { def a = "c" ; override def s = "C" }
defined trait C

scala> new C {}.s
res0: String = C

scala> new A with B { def a = "ab" }.s
<console>:10: error: <$anon: A with B> inherits conflicting members:
  method s in trait A of type => String  and
  method s in trait B of type => String
(Note: this can be resolved by declaring an override in <$anon: A with B>.)
              new A with B { def a = "ab" }.s
                  ^

scala> new A with B { def a = "ab" ; override def s = super[B].s }.s
res2: String = Bab

如果有的话,关键是 B.s 不会覆盖 A.s.

这不像其他答案那样具有激励性。

【讨论】:

  • 很好奇,_: A =&gt;this: A =&gt; 的意思一样吗?
  • @megri 显然。 trait B { _ =&gt; def b = 42 ; class C { def c = `_`.b } }
【解决方案3】:

泛型参数必须是类型本身:

trait Gen[T] {self : T => ...}

我看不出如何在 java 或 C# 中获得此约束。然而,它可以近似为

trait Gen[T] {
   def asT : T // abstract
}

【讨论】:

    【解决方案4】:

    还有,

    对于self类型,它需要一个trait来混入。它不能使用类或对象。奇怪的是它允许 define 一个 class 可以与 class 混合,但它只会在您尝试 instantiate 时编译失败 它。看到这个问题:

    why self-type class can declare class

    【讨论】:

      【解决方案5】:

      最大的区别在于您最终得到的公共界面。让我们以您给出的示例为例(稍微简化):

      trait Fooable { def foo: String = "foo" }
      trait Bar1 { self: Fooable =>
        def Y = foo + "-bar"
      }
      trait Bar2 extends Fooable {
        def Y = foo + "-bar"
      }
      // If we let type inference do its thing we would also have foo() in the public interface of b1, but we can choose to hide it
      def b1:Bar1 = new Bar1 with Fooable
      // b2 will always have the type members from Bar2 and Fooable
      def b2:Bar2 = new Bar2{}
      
      // Doesn't compile - 'foo' definition is only visible inside the definition of Bar1
      println(b1.foo)
      // Compiles - 'foo' definition is visible outside the definition of Bar2
      println(b2.foo)
      

      因此,如果您想使用 trait 的功能而不不必要地让您的客户知道您正在混合该 trait,那么您应该使用 self-type 注释。

      自类型注解不暴露底层类型的公共接口。 扩展另一个类型总是暴露父类型的公共接口。

      【讨论】:

        猜你喜欢
        • 2021-08-22
        • 2016-07-30
        • 2014-11-14
        • 1970-01-01
        • 2019-03-23
        • 1970-01-01
        • 2016-03-27
        • 1970-01-01
        • 2019-05-06
        相关资源
        最近更新 更多