Подтип в 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.