자기 유형과 특성 서브 클래스의 차이점은 무엇입니까?
형질의 자기 유형 A
:
trait B
trait A { this: B => }
말한다 " A
또한 연장하지 않는 구체적인 클래스로 함께 사용할 수 없습니다 B
" .
반면에 다음은
trait B
trait A extends B
그 말한다 "어떤 (콘크리트 또는 추상) 클래스에 혼합 A
또한 B에 혼합 될 것입니다" .
이 두 진술이 같은 의미가 아닙니까? self-type은 단순한 컴파일 타임 오류 가능성을 만드는 데만 사용됩니다.
내가 무엇을 놓치고 있습니까?
케이크 패턴과 같이 의존성 주입에 주로 사용됩니다 . 케이크 패턴을 포함하여 스칼라에는 다양한 형태의 의존성 주입을 다루는 훌륭한 기사 가 있습니다 . Google "케이크 패턴 및 스칼라"를 사용하면 프리젠 테이션 및 비디오를 포함한 많은 링크가 제공됩니다. 지금은 다른 질문에 대한 링크 입니다.
자아 유형과 특성을 확장하는 것의 차이점은 간단합니다. 당신이 말한다면 B extends 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
하여 a를 혼합해야하는 요구 사항 User
이 충족됩니다. 그러나 위에서 언급 한 두 번째 요구 사항은 충족되지 않습니다. 구현의 부담은 User
여전히 확장 된 클래스 / 특성에 남아 있습니다 Right
.
함께 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
Odersky 책에서 33.5 (스프레드 시트 UI 생성 장) 섹션을 참조하십시오.
스프레드 시트 예제에서 Model 클래스는 Evaluator에서 상속되므로 평가 방법에 액세스 할 수 있습니다. 다른 방법으로, Evaluator 클래스는 다음과 같이 자체 유형을 모델로 정의합니다.
package org.stairwaybook.scells
trait Evaluator { this: Model => ...
도움이 되었기를 바랍니다.
또 다른 차이점은 자체 유형이 비 클래스 유형을 지정할 수 있다는 것입니다. 예를 들어
trait Foo{
this: { def close:Unit} =>
...
}
여기서 자기 유형은 구조적 유형입니다. 그 결과 Foo에서 믹스되는 것은 arg가없는 "close"메소드 리턴 유닛을 구현해야한다는 것입니다. 이것은 오리 타이핑을위한 안전한 믹스 인을 허용합니다.
Martin Odersky의 원본 Scala paper Scalable Component Abstractions 의 2.3 절 "Selftype Annotations"는 실제로 믹스 인 구성을 넘어서는 셀프 타이프의 목적을 잘 설명합니다. 클래스를 추상적 인 유형과 연결하는 다른 방법을 제공하십시오.
논문에 제시된 예는 다음과 같으며, 우아한 서브 클래스 통신원이없는 것 같습니다.
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
. 그러나 Cow를 확장하는 특성은 그러한 권한이 없습니다. 한편, 누구에게나class Cow extends FourStomachs
노출digestGrass
됩니다extends Cow
.자체 유형은 주기적 종속성을 허용하지만 다른 유형은 확장하지 않습니다.
주기적 의존성부터 시작하겠습니다.
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
자체 유형의 멤버를 대체하면 상속을 사용하여 super를 통해 액세스 할 수있는 원래 멤버에 대한 액세스 권한이 손실됩니다. 따라서 상속을 통해 실제로 얻는 것은 다음과 같습니다.
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
이제 케이크 패턴의 모든 미묘함을 이해한다고 주장 할 수는 없지만, 모듈성을 적용하는 주된 방법은 상속이나 자기 유형보다는 구성을 통한 것입니다.
상속 버전은 짧지 만 자체 유형보다 상속을 선호하는 주된 이유는 자체 유형으로 초기화 순서를 올바르게 만드는 것이 훨씬 까다로워지기 때문입니다. 그러나 상속으로 할 수없는 자기 유형으로 할 수있는 일이 있습니다. 상속에는 다음과 같이 특성이나 클래스가 필요하지만 자체 유형은 유형을 사용할 수 있습니다.
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 }
더 공감해야 할 한 가지 점은 특성이 수업을 확장 할 수 있다는 것입니다. 이것을 지적 해 주신 David Maclver에게 감사드립니다. 내 코드의 예는 다음과 같습니다.
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
그런 다음 클래스 는 자체에서 상속받을 수있는 여러 직교 특성으로 상속 될 수 있습니다. 여러 유형의 매개 변수 (일반)를 사용하면 다른 형태의 모듈성이 제공됩니다.
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 type의 특성이있는 Closeable
경우 해당 특성은 혼합 할 수있는 유일한 항목이 Closeable
인터페이스 를 구현해야한다는 것을 알고 있습니다.
업데이트 : 주요 차이점은 셀프 유형이 여러 클래스에 따라 다를 수 있다는 것입니다 (약간의 경우입니다). 예를 들어
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
되거나 그 반대의 경우에만 의미가 있습니다. 요점은 자체 유형을 사용 Employee
하는 것이 의존하는 클래스의 계층 구조와 독립적 일 수 있다는 것입니다. 무엇을 확장하는지는 중요하지 않습니다. Expense
vs 의 계층 구조를 전환하면 Person
수정할 필요가 없습니다 Employee
.
첫 번째 경우, B의 하위 특성 또는 하위 클래스는 A를 사용하는 모든 것에 혼합 될 수 있습니다. 따라서 B는 추상 특성이 될 수 있습니다.
'development' 카테고리의 다른 글
MVC4의 스타일 (0) | 2020.02.26 |
---|---|
인스턴스를 '부정'하는 가장 좋은 방법 (0) | 2020.02.26 |
알려진 값 세트를 사용하여 정수가 두 정수 (포함) 사이에 있는지 판별하는 가장 빠른 방법 (0) | 2020.02.26 |
Chrome 디버거에 '주의 : 임시 헤더가 표시됨' (0) | 2020.02.26 |
정적 링크와 동적 링크 (0) | 2020.02.26 |