Trait 继承和 self 类型注释之间的区别

发布于 2024-08-20 21:16:47 字数 212 浏览 12 评论 0原文

在 Scala 中,我已经看到了构造

trait T extends S

trait T { this: S =>

用于实现类似的事情(即必须在创建实例之前定义 S 中的抽象方法)。他们之间有什么区别?为什么你会使用其中一种而不是另一种?

In Scala, I've seen the constructs

trait T extends S

and

trait T { this: S =>

used to achieve similar things (namely that the abstract methods in S must be defined before an instance may be created). What's the difference between them? Why would you use one over the other?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

秋风の叶未落 2024-08-27 21:16:47

自类型注释允许您表达循环依赖关系。例如:

trait A extends B
trait B { self: A => }

这对于简单继承来说是不可能的。

Self type annotations allow you to express cyclic dependencies. For instance:

trait A extends B
trait B { self: A => }

This is not possible with simple inheritance.

·深蓝 2024-08-27 21:16:47

我会使用自我类型进行依赖管理:这个特征需要混合另一个特征。并且我会使用继承来完善另一个特征或接口。

举个例子:

trait FooService

trait FooRemoting { this : FooService => }
trait FooPersistence { this : FooService => }

object Services extends FooService with FooRemoting with FooPersistence

现在,如果 FooRemoting 和 FooPersistence 都继承自 FooService,并且 FooService 有成员和方法,那么 Services 会是什么样子?

而对于继承,我们会有类似的东西:

trait Iterator[T] {
  def hasNext : boolean
  def next : T
}

trait InfiniteIterator[T] extends Iterator[T] {
  def hasNext = true
}

I'd use self-types for dependency-management: This trait requires another trait to be mixed in. And I'd use inheritance to refine another trait or interface.

Just as an example:

trait FooService

trait FooRemoting { this : FooService => }
trait FooPersistence { this : FooService => }

object Services extends FooService with FooRemoting with FooPersistence

Now, if FooRemoting and FooPersistence both would have inherited from FooService, and FooService has members and methods, how would Services look like?

Whereas for inheritance, we'd have something like:

trait Iterator[T] {
  def hasNext : boolean
  def next : T
}

trait InfiniteIterator[T] extends Iterator[T] {
  def hasNext = true
}
习惯成性 2024-08-27 21:16:47

自从提出问题以来,我遇到了这些帖子:

Spiros Tzavellas 讨论了使用特征作为公共接口,使用自我类型作为必须由实现类混合的帮助器。

总之,如果我们想搬家
特征内部的方法实现
那么我们就有污染界面的风险
用抽象方法描述这些特征
支持实施
具体方法和方法无关
主要负责人
特征。这个问题的解决方案是
将那些抽象方法移入
其他特征并组成特征
一起使用自我类型注释
和多重继承。

例如:

trait PublicInterface { this: HelperTrait =>
  // Uses helperMethod
}

trait HelperTrait {
  def helperMethod = // ...
}

class ImplementationClass extends PublicInterface with HelperTrait

Scala 之旅 讨论使用带有抽象类型成员的自类型注释 - 大概是无法扩展抽象类型成员(?)

Since asking the question I came across these posts:

Spiros Tzavellas talks about using a trait as the public interface and the self type as a helper that must be mixed in by the implementation class.

In conclusion, if we want to move
method implementations inside traits
then we risk polluting the interface
of those traits with abstract methods
that support the implementation of the
concrete methods and are unrelated
with the main responsibility of the
trait. A solution to this problem is
to move those abstract methods in
other traits and compose the traits
together using self type annotations
and multiple inheritance.

For example:

trait PublicInterface { this: HelperTrait =>
  // Uses helperMethod
}

trait HelperTrait {
  def helperMethod = // ...
}

class ImplementationClass extends PublicInterface with HelperTrait

A Tour of Scala discusses using self type annotations with abstract type members - presumably it's not possible to extend an abstract type member(?)

素年丶 2024-08-27 21:16:47

我知道这个问题很旧,但我想添加一些说明和示例。

特质遗传和自我类型之间存在三个主要区别。

语义

继承是对象范式中耦合度最高的关系之一,如果 A 扩展 B,则意味着 A 是 B。

假设我们有以下代码,

trait Animal {
  def stop():Unit = println("stop moving")
}

class Dog extends Animal {
  def bark:String = "Woof!"
}

val goodboy:Dog = new Dog

goodboy.bark
// Woof!

我们说狗 一只动物。我们可以将消息 barkstop 发送给 goodboy,因为它是一只狗,它理解这两种方法。

现在假设我们有一个新特征,

trait Security {
  this: Animal =>
  def lookout:Unit = { stop(); println("looking out!") }
}

这次安全不是动物,这很好,因为如果我们确认安全是动物,那么在语义上就会不正确,它们是不同的概念,可以一起使用。

现在我们可以创建一种新的狗,

val guardDog = new Dog with Security

guardDog.lookout
// stop moving
// looking out!

guardDog 是一只狗,一种动物和安全。它理解停止吠叫监视,因为它是一只有安全感的狗。

但是如果我们创造一只这样的新狗会发生什么呢?

val guardDog2:Dog = new Dog with Security
guardDog2.lookout // no such method!

guardDog2 只是一只 Dog,因此我们无法调用 lookout 方法。 (好吧,这是一只具有安全性的狗,但我们只看到一只狗)

循环依赖

自类型允许我们在类型之间创建循环依赖。

trait Patient {
  this: Reader =>
  def isQuite:Boolean = isReading
  def isSlow:Boolean = true
}

trait Reader {
  this: Patient =>
  def read():Unit = if(isSlow) println("Reading Slow...") else println("Reading Fast...")
  def isReading = true
}

val person = new Patient with Reader

以下代码无法编译。

trait Patient extends Reader { /** code **/}

trait Reader extends Patient { /** code **/ }

这种代码在依赖注入(蛋糕模式)中很常见。

多功能性

最后但并非最不重要的一点是,谁使用我们的特征可以决定它们的使用顺序,因此由于特征线性化,尽管使用的特征相同,但最终结果可能会有所不同。

对于正常的继承,我们不能这样做,特征和类之间的关系是固定的。

trait Human {
  def isGoodForSports:Boolean
}

trait Programmer extends Human {
  def readStackOverflow():Unit = println("Reading...")
  override def isGoodForSports: Boolean = false
}

trait Sportsman extends Human {
  def play():Unit = println("Playing something")
  override def isGoodForSports: Boolean = true
}

val foo = new Programmer with Sportsman
foo.isGoodForSports
// true

val bar = new Sportsman with Programmer
bar.isGoodForSports
// false

希望这会有用。

I know this question is old but I would like to add some clarification and examples.

There are three main differences between trait inheritance and self types.

Semantics

Inheritance is one of the relationships with the most coupling of the object paradigm, if A extends B, that means that A is a B.

Let's say we have the following code,

trait Animal {
  def stop():Unit = println("stop moving")
}

class Dog extends Animal {
  def bark:String = "Woof!"
}

val goodboy:Dog = new Dog

goodboy.bark
// Woof!

We are saying that a Dog is an Animal. We can send the messages bark and stop to goodboy because is a Dog, it understand both methods.

Now suppose we have a new trait,

trait Security {
  this: Animal =>
  def lookout:Unit = { stop(); println("looking out!") }
}

This time Security is NOT an Animal, and that is fine because would be semantically incorrect if we affirm that a Security is an Animal, they are different concepts, that can be used together.

So now we can create a new kind of dog,

val guardDog = new Dog with Security

guardDog.lookout
// stop moving
// looking out!

guardDog is a Dog, an Animal and Security. It understand stop, bark and lookout because is a Dog with Security.

But what happens if we create a new dog like this?

val guardDog2:Dog = new Dog with Security
guardDog2.lookout // no such method!

guardDog2 is just a Dog, so we can't call lookout method. (okok, it's a Dog with Security, but we just see a Dog)

Cyclic Dependencies

Self Types allow us to create cyclic dependencies between types.

trait Patient {
  this: Reader =>
  def isQuite:Boolean = isReading
  def isSlow:Boolean = true
}

trait Reader {
  this: Patient =>
  def read():Unit = if(isSlow) println("Reading Slow...") else println("Reading Fast...")
  def isReading = true
}

val person = new Patient with Reader

The following code doesn't compile.

trait Patient extends Reader { /** code **/}

trait Reader extends Patient { /** code **/ }

This kind of code is very common in dependency injection (cake pattern).

Versatility

Last but not least, who uses our traits can decide the order in which they are used, so thanks to Trait Linearization the final result can be different although the traits used are the same.

With normal inheritance we can't do that, the relations between traits and classes are fixed.

trait Human {
  def isGoodForSports:Boolean
}

trait Programmer extends Human {
  def readStackOverflow():Unit = println("Reading...")
  override def isGoodForSports: Boolean = false
}

trait Sportsman extends Human {
  def play():Unit = println("Playing something")
  override def isGoodForSports: Boolean = true
}

val foo = new Programmer with Sportsman
foo.isGoodForSports
// true

val bar = new Sportsman with Programmer
bar.isGoodForSports
// false

Hope this can be useful.

無心 2024-08-27 21:16:47

答案是“循环”。但不仅如此。

自类型注释为我解决了继承的基本问题:你继承的东西不能使用你的东西。
有了自我型,一切都变得容易了。

我的模式如下,可以被认为是一个退化的蛋糕:

trait A { self: X => def a = reuseme}
trait B { self: X => def b = a }
class X extends A with B { def reuseme=null }

您可以以多种行为分解您的类,这些行为可以从程序集中的任何位置调用,同时保持干净的类型。
不需要经常(并且错误地)将蛋糕图案与痛苦的间接联系起来。

过去十年中,一半(如果不是全部)复杂的 Java DI 框架都致力于做到这一点,当然不需要打字。
仍在该领域使用 JAVA 的人们显然正在浪费时间:“SCALA ouakbar”。

The answer is "circularity". But not only.

Self type annotation solves for me the fundamental problem of inheritance: what you inherit from cannot use what you are.
With the self type, everything becomes easy.

My pattern is the following and can be considered as a degenerated cake:

trait A { self: X => def a = reuseme}
trait B { self: X => def b = a }
class X extends A with B { def reuseme=null }

You can explode your class in multiple behaviours which can be called from anywhere in the assembly, while staying cleanly typed.
No need for the painful indirection too often (and wrongly) identified with the cake pattern.

Half (if not the totality) of the convoluted Java DI frameworks of the last ten years have been devoted to do this, of course without the typing.
People still using JAVA in this domain are clearly loosing their time: "SCALA ouakbar".

沙沙粒小 2024-08-27 21:16:47

虽然它没有回答你的问题,但我试图理解自我类型注释,基本上迷失在答案中,并且不知何故最终循环了你的问题的变体,该问题侧重于使用自我类型注释来声明依赖关系。

因此,我在这里发布了一个用例的描述,其中很好地说明了自类型注释,即类似于“this”作为子类型的类型安全情况:

http://programming-scala.labs.oreilly.com/ch13.html#SelfTypeAnnotationsAndAbstractTypeMembers

希望这对最终的人有帮助偶然回答这个问题(并且像我一样,在开始探索之前没有时间阅读 scala 书:-))

Although it doesn't answer your question, I was trying to understand the self-type annotations and basically got lost in answers, and somehow ended up cycling through variations of your question, which focuses on usage of self-type annotations for stating dependencies.

So here I post a description of an use case where self-type annotations are well illustrated, namely something like a type-safe case of 'this' as a subtype:

http://programming-scala.labs.oreilly.com/ch13.html#SelfTypeAnnotationsAndAbstractTypeMembers

hoping that it would be helpful to those who end up on this question by chance (and, like me, didn't have time to read a scala book before starting to explore :-) )

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文