WAMP Server в первую очередь предназначен для разработки PHP / MySQL в Windows и не поддерживает Java / JSP из коробки. (AFAIK) Хотя можно было бы использовать ту же установку WAMP для ее настройки для поддержки JSP, вы бы по-прежнему нужен сервлет-контейнер для интерпретации исходных файлов Java / JSP. Поскольку вы новичок в WAMP, было бы логичнее установить другой контейнер сервлетов, такой как Tomcat, приведенный ниже, чтобы сократить время, необходимое для запуска и запуска.
Развернуть JSP-приложения с минимальной конфигурацией /
http://tomcat.apache.org/ - Загрузить Tomcat 6.0 (служба Windows)
Используется преимущественно для Впрыска зависимостей , например, в образце торта. Существует большая статья , охватывающая множество различных форм введения зависимости в Scala, в том числе в Cake Pattern. Если вы нажмете Google "Картина пирога и Scala", вы получите множество ссылок, в том числе презентации и видеоролики. А пока, вот ссылка на другой вопрос .
Теперь, что касается разницы между самотипом и расширением признака, это просто. Если вы скажете, что B расширяет A
, то B
будет и A
. При использовании автотипов B
требуется A
. Существуют два особых требования, которые создаются с использованием самотипов:
B
является расширенным, то вам требуется для смешивания с A
. 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
.
Для более практического использования, пожалуйста, ознакомьтесь со ссылками в начале данного ответа! Но, надеюсь, теперь вы получите их.
Самостоятельные типы позволяют вам определять циклические зависимости. Например, вы можете добиться этого:
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 => ...
Надеюсь, это поможет.
Раздел 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;
}
}
Еще одна вещь, которая не была упомянута: поскольку самотипы не являются частью иерархии требуемого класса, их можно исключить из сопоставления с образцом, особенно если вы исчерпывающе сопоставляете запечатанную иерархию. Это удобно, когда вы хотите смоделировать ортогональное поведение, например:
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
TL; DR краткий обзор других ответов:
Типы, которые вы расширяете, подвержены унаследованным типам, но самотипы не являются
, например: class Cow { this: FourStomachs }
позволяет использовать методы, доступные только жвачным, например, digestGrass
. Черты, которые расширяют корову, однако, не будут иметь таких привилегий. С другой стороны, class Cow extends FourStomachs
откроет digestGrass
любому, кто extends Cow
.
self-типы допускают циклические зависимости, расширение других типов не
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
}
Самостоятельный тип позволяет вам указать, какие типы могут смешиваться в признаке. Например, если у вас есть черта с типом self Closeable
, то эта черта знает, что единственные вещи, которые могут смешивать ее, должны реализовывать интерфейс Closeable
.
В первом случае под-признак или подкласс B может быть смешан с тем, что использует A. Таким образом, B может быть абстрактной чертой.
Давайте начнем с циклической зависимости.
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
может затем наследоваться множеством ортогональных признаков, которые сами могут наследоваться. Использование параметров нескольких типов (обобщений) предлагает альтернативную форму модульности.
Обновление: Принципиальным отличием является то, что 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
.
Еще одно отличие состоит в том, что самотипы могут указать неклассовые типы. Например
trait Foo{
this: { def close:Unit} =>
...
}
Самотип здесь является структурным типом. Эффект состоит в том, чтобы сказать, что все, что смешивается в Foo, должно реализовывать блок, возвращающий метод "close" без аргументов. Это позволяет создавать безопасные миксины для утиного набора.