Това не е само защото A with B
трябва да се разглежда като нов тип. За системата от тип Scala няма пряко значение дали съществува клас, съответстващ на A with B
. Анонимен клас се генерира, защото трябва да съдържа мостови методи за всички методи в характеристиките, които са били смесени.
Причината за създаването на анонимен клас е, че обектът трябва да има реализации на всички методи от A
и всички методи от B
. На ниво байткод на JVM това би гарантирало наследяване на множество класове, а моделът на множествено наследяване не се поддържа от JVM.
За да симулира множествено наследяване (или миксин композиция, както искате да го наречете), Scala прави следните неща, когато създавате черта:
- Ако чертата
T
няма реализации на метод, тя създава интерфейс, който дефинира всички методи в чертата.
Ако характеристиката T
има реализации на метод, тя допълнително създава клас T$class
, който има статичен метод за всеки от конкретните методи в T
. Този статичен метод има същото тяло като съответния му метод в T
, но неговият подпис е променен, за да включва параметъра this
. Ако T
имаше:
def foo(x: Int) = x
тогава T$class
ще има:
<static> def foo($this: T, x: Int) = x
Класът, получен чрез композиция на смесване на някакъв клас A
и някаква черта T
, след това ще има генериран специален мостов метод, който препраща извикването към статичния метод, който съдържа тялото. По този начин тялото на метода не се дублира във всеки клас, който се смесва в T
. Ето защо анонимният клас трябва да бъде създаден - той трябва да има мостови методи, дефинирани за всеки метод в T
.
Ето един пример. Когато създавате нов клас, като правите миксин композиция, напр. обадете се на new A with T
:
class A {
def bar = println("!")
}
trait T {
def foo(x: Int) = x
}
new A with T
компилаторът ще го пренапише грубо до нещо подобно:
class A {
def bar = println("!")
}
<interface> T {
def foo(x: Int): Int
}
class T$class {
<static> def foo($this: T, x: Int) = x
}
class $anon extends A <implements> T {
// notice that `bar` is inherited, but `foo` is not
<bridge> def foo(x: Int) = T$class.foo(this, x)
}
new $anon
Забележете, че компилаторът всъщност може да пренапише сайтовете за извикване на foo
, за да извика статичните методи директно от сайта за извикване, а не чрез мостов метод. Причината, поради която не е направено по този начин, е, че тогава вече няма да поддържа подтипов полиморфизъм.
person
axel22
schedule
28.06.2012