Разница между наследованием признака и аннотацией собственного типа - PullRequest
41 голосов
/ 09 февраля 2010

В Scala я видел конструкции

trait T extends S

и

trait T { this: S =>

, используемые для достижения подобных вещей (а именно, абстрактные методы в S должны быть определены доэкземпляр может быть создан).Какая разница между ними?Почему вы используете один поверх другого?

Ответы [ 6 ]

26 голосов
/ 09 февраля 2010

Самостоятельные аннотации типа позволяют выражать циклические зависимости. Например:

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

Это невозможно при простом наследовании.

14 голосов
/ 09 февраля 2010

Я бы использовал собственные типы для управления зависимостями: эта черта требует смешивания другой черты. И я бы использовал наследование для уточнения другой черты или интерфейса.

Просто в качестве примера:

trait FooService

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

object Services extends FooService with FooRemoting with FooPersistence

Теперь, если FooRemoting и FooPersistence оба унаследованы от FooService, а FooService имеет члены и методы, как бы выглядели службы?

В то время как для наследования у нас было бы что-то вроде:

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

trait InfiniteIterator[T] extends Iterator[T] {
  def hasNext = true
}
8 голосов
/ 09 февраля 2010

С тех пор, как задал вопрос, я наткнулся на эти сообщения:

Spiros Tzavellas говорит об использовании свойства в качестве открытого интерфейса и типа self в качестве помощника, который должен смешиваться классом реализации.

В заключение, если мы хотим двигаться реализации метода внутри черт тогда мы рискуем загрязнить интерфейс из этих черт с абстрактными методами которые поддерживают реализацию конкретные методы и не связаны с основной ответственностью черта характера. Решение этой проблемы переместить эти абстрактные методы в другие черты и составляют черты вместе, используя аннотации собственного типа и множественное наследование.

Например:

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

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

class ImplementationClass extends PublicInterface with HelperTrait

Путешествие по Scala обсуждается использование аннотаций собственного типа с членами абстрактного типа - предположительно, extend член абстрактного типа невозможно (?)

2 голосов
/ 08 сентября 2018

Я знаю, что этот вопрос старый, но я хотел бы добавить некоторые пояснения и примеры.

Существует три основных различия между наследованием признаков и типами Я.

Семантика

Наследование - это одно из отношений с наибольшей связью объектной парадигмы, если 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!

Мы говорим, что Собака является Животным. Мы можем отправлять сообщения bark и stop на 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 - собака, животное и охрана. Он понимает stop, bark и lookout, потому что это собака с безопасностью.

Но что произойдет, если мы создадим такую ​​новую собаку?

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

guardDog2 - это просто собака, поэтому мы не можем вызвать lookout метод. (окок, это собака с охраной, но мы просто видим собаку)

Циклические зависимости

Self 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

Следующий код не компилируется.

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

Надеюсь, это может быть полезно.

2 голосов
/ 24 декабря 2014

Ответ - "округлость". Но не только.

Самостоятельная аннотация типа решает для меня фундаментальную проблему наследования: то, что вы наследуете, не может использовать то, что вы есть. С типом self все становится просто.

Моя схема следующая и может рассматриваться как выродившийся пирог:

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".

1 голос
/ 11 декабря 2010

Хотя это и не отвечает на ваш вопрос, я пытался понять аннотации самопечатания и в основном заблудился в ответах, и каким-то образом закончил циклическим переходом по вариантам вашего вопроса, который фокусируется на использовании аннотаций самопечатания для с указанием зависимостей.

Итак, здесь я выкладываю описание варианта использования, в котором аннотации самопечатания хорошо проиллюстрированы, а именно что-то вроде безопасного типа «this» как подтипа:

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

надеясь, что это будет полезно для тех, кто случайно решит этот вопрос (и, как и я, не успел прочитать книгу о скалах, прежде чем приступить к изучению :-))

...