Подтип в Scala: что такое " тип X
может ли кто-нибудь объяснить подтип(
trait SwingApi {
type ValueChanged <: Event
val ValueChanged: {
def unapply(x: Event): Option[TextField]
}
type ButtonClicked <: Event
val ButtonClicked: {
def unapply(x: Event): Option[Button]
}
type TextField <: {
def text: String
def subscribe(r: Reaction): Unit
def unsubscribe(r: Reaction): Unit
}
type Button <: {
def subscribe(r: Reaction): Unit
def unsubscribe(r: Reaction): Unit
}
}
2 ответов
я знаю этот код! :)
так что давайте убедимся, что вы понимаете, что <:
значит, на всякий случай. A <: B
означает, что A
должен быть подтипом B
, или, другими словами, каждый экземпляр A
будет экземпляр B
также (но не наоборот).
мы знаем, например, что каждый класс в Java <: Object
(например,String <: Object
).
далее, Почему type ValueChanged <: Event
. Это обычно встречается в шаблоне торта, но я пропущу объяснение этого (в уроке упоминался шаблон торта и предоставлялась ссылка iirc).
это означает, что для всего, что простирается SwingApi
, типа ValueChanged
должен быть подтипом Event
. Это дает возможность позвонить Event
методы на все, что объявлено с типом ValueChanged
, не зная заранее точно, что это за тип.
что похож на следующий:
type TextField <: {
def text: String
def subscribe(r: Reaction): Unit
def unsubscribe(r: Reaction): Unit
}
мы заявляем здесь, что TextField
должны иметь эти методы, поэтому, когда мы получаем что-то типа TextField
(например, возвращенные ValueChanged
extractor), мы можем вызвать эти методы на нем. Мы могли бы написать такой код:
trait MyStuff extends SwingApi {
def subscribeTo(event: ValueChanged) = event match {
case ValueChanged(textField) => textField.subscribe(myReaction)
}
def myReaction: Reaction
}
на данный момент, ни SwingApi
, ни MyStuff
знать, какие типы будут использоваться для ValueChanged
или TextField
, и, тем не менее, они могут использовать их в нормальный код.
один интересный факт, который часто упускают из виду type
декларации заключается в том, что они могут быть переопределены занятия. То есть я могу написать что-то вроде этого:
class SwingImpl extends SwingApi {
class TextField {
def text: String = ???
def subscribe(r: Reaction): Unit = ???
def unsubscribe(r: Reaction): Unit = ???
}
// etc
}
наконец, вы можете задаться вопросом, что это за польза. Приведу один пример. Естественно, вы хотите, чтобы производственный код показывал графические элементы на экране и тому подобное, и, возможно, вы могли бы написать отдельный класс, который реализует его на веб-сервере. Но, и я думаю, конечно, использует его, вы можете написать класс, который реализует его не как то, что отображает эти компоненты, а как
в силу абстрактных членов типа ValueChanged
и ButtonClicked
, trait SwingApi
сам по себе деинсталлируется (все черты, но если они полностью реализованы, они тривиально превращаются в конкретный класс, который может быть создан).
эти ограничения говорят, что инстанцируемые подтипы SwingApi
определить ValueChanged
и ButtonClicked
как подтипы Event
.
тип псевдонимы TextField
и Button
ограничены как структурные типы (им не нужно определенное связь подкласса, но просто должна предоставлять указанным членам указанные типы).
на почему это просто общность. Это накладывает минимальные ограничения на исполнителей trait SwingApi
необходимо, чтобы он использовался кодом, который требует SwingApi
.