В чем разница между типами self и подклассами черт?

WAMP Server в первую очередь предназначен для разработки PHP / MySQL в Windows и не поддерживает Java / JSP из коробки. (AFAIK) Хотя можно было бы использовать ту же установку WAMP для ее настройки для поддержки JSP, вы бы по-прежнему нужен сервлет-контейнер для интерпретации исходных файлов Java / JSP. Поскольку вы новичок в WAMP, было бы логичнее установить другой контейнер сервлетов, такой как Tomcat, приведенный ниже, чтобы сократить время, необходимое для запуска и запуска.

Развернуть JSP-приложения с минимальной конфигурацией /

http://tomcat.apache.org/ - Загрузить Tomcat 6.0 (служба Windows)

374
задан Jacek Laskowski 23 December 2014 в 23:02
поделиться

11 ответов

Используется преимущественно для Впрыска зависимостей , например, в образце торта. Существует большая статья , охватывающая множество различных форм введения зависимости в Scala, в том числе в Cake Pattern. Если вы нажмете Google "Картина пирога и Scala", вы получите множество ссылок, в том числе презентации и видеоролики. А пока, вот ссылка на другой вопрос .

Теперь, что касается разницы между самотипом и расширением признака, это просто. Если вы скажете, что B расширяет A, то B будет и A. При использовании автотипов B требуется A. Существуют два особых требования, которые создаются с использованием самотипов:

  1. Если B является расширенным, то вам требуется для смешивания с A.
  2. Когда класс бетона, наконец, расширяется/смешивается в этих признаках, некоторые классы/дорожки должны реализовать A.

Рассмотрим следующие примеры:

scala> trait User { def name: String }
defined trait User

scala> trait Tweeter {
     |   user: User =>
     |   def tweet(msg: String) = println(s"$name: $msg")
     | }
defined trait Tweeter

scala> trait Wrong extends Tweeter {
     |   def noCanDo = name
     | }
<console>:9: error: illegal inheritance;
 self-type Wrong does not conform to Tweeter's selftype Tweeter with User
       trait Wrong extends Tweeter {
                           ^
<console>:10: error: not found: value name
         def noCanDo = name
                       ^

Если бы Tweeter был подклассом User, ошибки не было бы. В приведенном выше коде мы требовали пользователя User всякий раз, когда используется Tweeter, однако User не был предусмотрен для Wrong, таким образом, мы получили ошибку. Теперь, когда вышеприведенный код все еще в поле зрения, рассмотрим:

scala> trait DummyUser extends User {
     |   override def name: String = "foo"
     | }
defined trait DummyUser

scala> trait Right extends Tweeter with User {
     |   val canDo = name
     | }
defined trait Right 

scala> trait RightAgain extends Tweeter with DummyUser {
     |   val canDo = name
     | }
defined trait RightAgain

С помощью Right, требование смешивания в User выполнено. Однако второе требование, упомянутое выше, не выполнено: бремя реализации Пользователь по-прежнему остается за классами/областями, которые расширяют Право.

С помощью RightAgain удовлетворяются оба требования. Предоставляется User и реализация User.

Для более практического использования, пожалуйста, ознакомьтесь со ссылками в начале данного ответа! Но, надеюсь, теперь вы получите их.

264
ответ дан 22 November 2019 в 23:59
поделиться

Самостоятельные типы позволяют вам определять циклические зависимости. Например, вы можете добиться этого:

trait A { self: B => }
trait B { self: A => }

Наследование с использованием extends не позволяет этого. Попробуйте:

trait A extends B
trait B extends A
error:  illegal cyclic reference involving trait A

В книге Одерского посмотрите раздел 33.5 (Глава «Создание пользовательского интерфейса электронной таблицы»), в котором упоминается:

В примере электронной таблицы класс Model наследует от Evaluator и таким образом получает доступ к его методу оценки. Чтобы пойти другим путем, класс Evaluator определяет свой собственный тип как Model:

package org.stairwaybook.scells
trait Evaluator { this: Model => ...

Надеюсь, это поможет.

153
ответ дан Jacek Laskowski 23 December 2014 в 23:02
поделиться

Раздел 2.3 «Аннотации к самотипу» оригинальной статьи Scala Мартина Одерского Абстракции масштабируемых компонентов на самом деле очень хорошо объясняет цель самотипа помимо миксиновой композиции: предоставляет альтернативный способ ассоциирования класса с абстрактным типом.

Пример, приведенный в статье, был похож на следующий, и у него, похоже, нет элегантного корреспондента подкласса:

abstract class Graph {
  type Node <: BaseNode;
  class BaseNode {
    self: Node =>
    def connectWith(n: Node): Edge =
      new Edge(self, n);
  }
  class Edge(from: Node, to: Node) {
    def source() = from;
    def target() = to;
  }
}

class LabeledGraph extends Graph {
  class Node(label: String) extends BaseNode {
    def getLabel: String = label;
    def self: Node = this;
  }
}
13
ответ дан lcn 23 December 2014 в 23:02
поделиться

Еще одна вещь, которая не была упомянута: поскольку самотипы не являются частью иерархии требуемого класса, их можно исключить из сопоставления с образцом, особенно если вы исчерпывающе сопоставляете запечатанную иерархию. Это удобно, когда вы хотите смоделировать ортогональное поведение, например:

sealed trait Person
trait Student extends Person
trait Teacher extends Person
trait Adult { this : Person => } // orthogonal to its condition

val p : Person = new Student {}
p match {
  case s : Student => println("a student")
  case t : Teacher => println("a teacher")
} // that's it we're exhaustive
12
ответ дан Bruno Bieth 23 December 2014 в 23:02
поделиться

TL; DR краткий обзор других ответов:

  • Типы, которые вы расширяете, подвержены унаследованным типам, но самотипы не являются

    , например: class Cow { this: FourStomachs } позволяет использовать методы, доступные только жвачным, например, digestGrass. Черты, которые расширяют корову, однако, не будут иметь таких привилегий. С другой стороны, class Cow extends FourStomachs откроет digestGrass любому, кто extends Cow.

  • self-типы допускают циклические зависимости, расширение других типов не

10
ответ дан jazmit 23 December 2014 в 23:02
поделиться
trait A { def x = 1 }
trait B extends A { override def x = super.x * 5 }
trait C1 extends B { override def x = 2 }
trait C2 extends A { this: B => override def x = 2}

// 1.
println((new C1 with B).x) // 2
println((new C2 with B).x) // 10

// 2.
trait X {
  type SomeA <: A
  trait Inner1 { this: SomeA => } // compiles ok
  trait Inner2 extends SomeA {} // doesn't compile
}
7
ответ дан Oleg Galako 23 December 2014 в 23:02
поделиться

Самостоятельный тип позволяет вам указать, какие типы могут смешиваться в признаке. Например, если у вас есть черта с типом self Closeable, то эта черта знает, что единственные вещи, которые могут смешивать ее, должны реализовывать интерфейс Closeable.

4
ответ дан Blaisorblade 23 December 2014 в 23:02
поделиться

В первом случае под-признак или подкласс B может быть смешан с тем, что использует A. Таким образом, B может быть абстрактной чертой.

0
ответ дан IttayD 23 December 2014 в 23:02
поделиться

Давайте начнем с циклической зависимости.

trait A {
  selfA: B =>
  def fa: Int }

trait B {
  selfB: A =>
  def fb: String }

Тем не менее, модульность этого решения не так велика, как может показаться на первый взгляд, потому что вы можете переопределить собственные типы следующим образом:

trait A1 extends A {
  selfA1: B =>
  override def fb = "B's String" }
trait B1 extends B {
  selfB1: A =>
  override def fa = "A's String" }
val myObj = new A1 with B1

Хотя, если вы переопределите член При самостоятельном вводе вы теряете доступ к исходному члену, доступ к которому по-прежнему возможен через супер, используя наследование. Итак, что действительно выигрывает от использования наследования:

trait AB {
  def fa: String
  def fb: String }
trait A1 extends AB
{ override def fa = "A's String" }        
trait B1 extends AB
{ override def fb = "B's String" }    
val myObj = new A1 with B1

Теперь я не могу утверждать, что понимаю все тонкости шаблона торта, но мне кажется, что основной метод обеспечения модульности - это скорее композиция чем наследование или самопознание.

Версия наследования короче, но главная причина, по которой я предпочитаю наследование по сравнению с типами self, заключается в том, что мне гораздо сложнее получить правильный порядок инициализации с типами self. Однако есть некоторые вещи, которые вы можете делать с типами себя, которые вы не можете делать с наследованием. Сами типы могут использовать тип, в то время как наследование требует черты или класса, как в:

trait Outer
{ type T1 }     
trait S1
{ selfS1: Outer#T1 => } //Not possible with inheritance.

Вы даже можете сделать:

trait TypeBuster
{ this: Int with String => }

Хотя вы никогда не сможете создать его экземпляр , Я не вижу какой-либо абсолютной причины неспособности наследовать от типа, но я, конечно, чувствую, что было бы полезно иметь классы и свойства конструктора путей, поскольку у нас есть свойства / классы конструктора типов. Как, к сожалению,

trait InnerA extends Outer#Inner //Doesn't compile

У нас есть это:

trait Outer
{ trait Inner }
trait OuterA extends Outer
{ trait InnerA extends Inner }
trait OuterB extends Outer
{ trait InnerB extends Inner }
trait OuterFinal extends OuterA with OuterB
{ val myV = new InnerA with InnerB }

Или это:

  trait Outer
  { trait Inner }     
  trait InnerA
  {this: Outer#Inner =>}
  trait InnerB
  {this: Outer#Inner =>}
  trait OuterFinal extends Outer
  { val myVal = new InnerA with InnerB with Inner }

Еще один момент, на который следует обратить особое внимание, заключается в том, что черты могут расширять классы. Спасибо Дэвиду Маклверу за указание на это. Вот пример из моего собственного кода:

class ScnBase extends Frame
abstract class ScnVista[GT <: GeomBase[_ <: TypesD]](geomRI: GT) extends ScnBase with DescripHolder[GT] )
{ val geomR = geomRI }    
trait EditScn[GT <: GeomBase[_ <: ScenTypes]] extends ScnVista[GT]
trait ScnVistaCyl[GT <: GeomBase[_ <: ScenTypes]] extends ScnVista[GT]

ScnBase наследуется от класса фрейма Swing , так что он может быть использован как собственный тип, а затем смешан в конце ( при создании экземпляра). Однако, val geomR необходимо инициализировать, прежде чем он будет использован для наследования черт. Таким образом, нам нужен класс для обеспечения предварительной инициализации geomR. Класс ScnVista может затем наследоваться множеством ортогональных признаков, которые сами могут наследоваться. Использование параметров нескольких типов (обобщений) предлагает альтернативную форму модульности.

9
ответ дан Peter Mortensen 23 December 2014 в 23:02
поделиться

Обновление: Принципиальным отличием является то, что self-типы могут зависеть от нескольких классов (я допускаю, что это немного угловой случай). Например, у вас может быть

class Person {
  //...
  def name: String = "...";
}

class Expense {
  def cost: Int = 123;
}

trait Employee {
  this: Person with Expense =>
  // ...

  def roomNo: Int;

  def officeLabel: String = name + "/" + roomNo;
}

Это позволяет добавить миксин Employee просто ко всему, что является подклассом Person и Expense. Конечно, это имеет смысл только в том случае, если Expense распространяется на Person или наоборот. Дело в том, что использование self-типов Employee может быть независимым от иерархии классов, от которых она зависит. Неважно, что расширяет что - Если вы переключаете иерархию Expense против Person, вам не нужно изменять Employee.

1
ответ дан Petr Pudlák 23 December 2014 в 23:02
поделиться

Еще одно отличие состоит в том, что самотипы могут указать неклассовые типы. Например

trait Foo{
   this: { def close:Unit} => 
   ...
}

Самотип здесь является структурным типом. Эффект состоит в том, чтобы сказать, что все, что смешивается в Foo, должно реализовывать блок, возвращающий метод "close" без аргументов. Это позволяет создавать безопасные миксины для утиного набора.

56
ответ дан 22 November 2019 в 23:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: