4

我试图提出一个组合场景,其中 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:我想我的特定问题是为什么各种蛋糕模式示例通常都在谈论必须使用自我类型而不是扩展。我还没有找到一个蛋糕模式场景不能和扩展一样好用

4

5 回答 5

7

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

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

// Illegal
trait C extends D
trait D extends C

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

于 2014-08-20T20:57:32.253 回答
3

还,

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

如果有的话,关键是 Bs 不会覆盖 As

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

于 2014-08-20T21:03:51.753 回答
2

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

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

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

trait Gen[T] {
   def asT : T // abstract
}
于 2014-08-20T21:36:03.393 回答
0

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

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 注释。

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

于 2015-03-27T10:29:58.613 回答
0

还,

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

为什么自类型类可以声明类

于 2014-08-21T01:59:05.703 回答