Дело не только в том, что A with B
следует рассматривать как новый тип. Для системы типов Scala не имеет прямого значения, существует ли класс, соответствующий A with B
. Анонимный класс создается, потому что он должен содержать методы bridge для всех методов в смешанных трейтах.
Причина создания анонимного класса заключается в том, что объект должен иметь реализации всех методов из 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
Обратите внимание, что компилятор может фактически переписать callsites на foo
, чтобы вызывать статические методы непосредственно из callsite, а не через метод моста. Причина, по которой это не делается таким образом, заключается в том, что тогда он больше не будет поддерживать полиморфизм подтипов.
person
axel22
schedule
28.06.2012