Я знаю, что этот вопрос старый, но я хотел бы добавить некоторые пояснения и примеры.
Существует три основных различия между наследованием признаков и типами Я.
Семантика
Наследование - это одно из отношений с наибольшей связью объектной парадигмы, если 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
Надеюсь, это может быть полезно.