복붙노트

[SCALA] 자기 유형과 특성 서브 클래스의 차이점은 무엇입니까?

SCALA

자기 유형과 특성 서브 클래스의 차이점은 무엇입니까?

형질 A에 대한 자체 유형 :

trait B
trait A { this: B => }

"A도 B를 확장하지 않는 구체적인 클래스로 혼합 될 수 없다"고 말했다.

한편, 다음과 같은 :

trait B
trait A extends B

"A에있는 (콘크리트 또는 추상) 클래스 혼합이 또한 B에 혼합 될 것이다"고 말했다.

이 두 문장은 같은 것을 의미하지 않는다? 셀프 형은 간단한 컴파일 타임 오류의 가능성을 만드는 데에만 봉사하는 것 같다.

나는 무엇을 놓치고?

해결법

  1. ==============================

    1.그것은 주로 같은 케이크 패턴과 같이 의존성 주입에 사용됩니다. 케이크 패턴을 포함하여 스칼라의 의존성 주입의 다양한 형태를 포함하는 훌륭한 문서가 존재한다. 당신은 "케이크 패턴 및 스칼라을"구글 경우, 프리젠 테이션 및 동영상을 포함하여 많은 링크를 얻을 것이다. 지금, 여기에 또 다른 질문에 대한 링크입니다.

    그것은 주로 같은 케이크 패턴과 같이 의존성 주입에 사용됩니다. 케이크 패턴을 포함하여 스칼라의 의존성 주입의 다양한 형태를 포함하는 훌륭한 문서가 존재한다. 당신은 "케이크 패턴 및 스칼라을"구글 경우, 프리젠 테이션 및 동영상을 포함하여 많은 링크를 얻을 것이다. 지금, 여기에 또 다른 질문에 대한 링크입니다.

    이제, 자기 특성 및 유형을 연장 차이 것과 같이, 이것은 간단하다. 당신은 B가 확장 말할 경우, B는 자기 유형을 사용하면 자기 종류로 만든 두 개의 특정 요구 사항이 있습니다, 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
                           ^
    

    트위터는 사용자의 서브 클래스 인 경우, 에러가 없을 것이다. 위의 코드에서, 우리는하지만 사용자가 잘못된에게 제공되지 않은, 트위터가 사용될 때마다 사용자 요구, 그래서 우리는 오류를 얻었다. 지금, 여전히 범위에서 위의 코드로, 고려 :

    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
    

    오른쪽으로, 요구 사항은 혼합-에 대한 사용자 만족입니다. 그러나, 위에서 언급 한 두 번째 요구 사항은 만족하지 : 사용자를 구현하는 부담이 여전히 클래스 / 오른쪽 확장 형질 남아있다.

    RightAgain으로 모두 요구 사항을 만족하고 있습니다. 사용자와 사용자의 구현이 제공됩니다.

    더 실제 사용 사례를 들어,이 답변의 시작의 링크를 참조하시기 바랍니다! 그러나, 잘하면 지금 당신은 그것을 얻을.

  2. ==============================

    2.셀프 유형은 순환 종속성을 정의 할 수 있습니다. 예를 들어, 당신이 이것을 달성 할 수있다 :

    셀프 유형은 순환 종속성을 정의 할 수 있습니다. 예를 들어, 당신이 이것을 달성 할 수있다 :

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

    상속이 허용하지 않습니다 연장 사용. 시험:

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

    Odersky 책, 그것은 언급 섹션 33.5 (스프레드 시트 UI 장을 만들기)를 보면 :

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

    도움이 되었기를 바랍니다.

  3. ==============================

    3.하나의 추가 차이점은 자기 타입이 아닌 클래스 유형을 지정할 수 있다는 것입니다. 예를 들면

    하나의 추가 차이점은 자기 타입이 아닌 클래스 유형을 지정할 수 있다는 것입니다. 예를 들면

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

    여기에 자기 유형은 구조 유형입니다. 효과는 인수 없음 "가까운"방법은 반환하지 장치를 구현해야합니다 푸에 혼합이 아무 말도하는 것입니다. 이 오리 타이핑 안전 유지 mixin 수 있습니다.

  4. ==============================

    4.실제로 아주 잘 믹스 인 구성을 넘어 selftype의 목적을 설명 마틴 오더 스키의 원래 스칼라 용지 확장 구성 요소 추상화의 "Selftype 주석"2.3 절 : 추상적 인 유형의 클래스를 연결하는 다른 방법을 제공합니다.

    실제로 아주 잘 믹스 인 구성을 넘어 selftype의 목적을 설명 마틴 오더 스키의 원래 스칼라 용지 확장 구성 요소 추상화의 "Selftype 주석"2.3 절 : 추상적 인 유형의 클래스를 연결하는 다른 방법을 제공합니다.

    신문에 주어진 예는 다음과 같았다, 그리고 우아한 서브 클래스 특파원을하지 않는 것 같습니다 :

    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;
      }
    }
    
  5. ==============================

    5.자기 유형은 당신이 철저하게 밀봉 된 계층 구조에 대해 일치, 특히 그들이 패턴 매칭에서 제외 할 수 있습니다 필요한 클래스의 계층 구조의 일부가 아닌 이유는 언급되지 않은 또 다른 것은. 이 같은 직교 행동을 모델링 할 때 편리합니다 :

    자기 유형은 당신이 철저하게 밀봉 된 계층 구조에 대해 일치, 특히 그들이 패턴 매칭에서 제외 할 수 있습니다 필요한 클래스의 계층 구조의 일부가 아닌 이유는 언급되지 않은 또 다른 것은. 이 같은 직교 행동을 모델링 할 때 편리합니다 :

    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
    
  6. ==============================

    6.TL; 다른 답변의 DR 요약 :

    TL; 다른 답변의 DR 요약 :

  7. ==============================

    7.의는 순환 종속성을 시작하자.

    의는 순환 종속성을 시작하자.

    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
    

    하지만 당신이 자기 형의 멤버를 오버라이드 (override)하는 경우, 당신은 여전히 ​​상속을 사용하여 슈퍼를 통해 액세스 할 수있는 원래의 멤버에 대한 액세스를 잃게됩니다. 그래서 정말 상속을 사용을 통해 얻게되는 것은 :

    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 }
    

    더 empathised해야 한 점은 특성 수있는 클래스를 확장한다는 것이다. 이 문제를 지적 데이비드 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는 스윙 프레임 클래스에서 상속, 그래서 자기 유형으로 사용하고 (인스턴스에서) 마지막에 혼합 할 수있다. 그러나, 발 geomR는이 특성을 상속에 의해 사용되기 전에 초기화해야합니다. 그래서 우리는 geomR의 사전 초기화를 시행하는 클래스가 필요합니다. 클래스 ScnVista은 다음 자체에서 상속 할 수있는 여러 직교 특성에 의해 상속 될 수있다. 여러 유형 매개 변수 (제네릭)을 사용하여 모듈 방식의 대안 양식을 제공합니다.

  8. ==============================

    8.

    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
    }
    
  9. ==============================

    9.자체 유형은이 특성을 믹스 인 할 수 있습니다 어떤 종류를 지정할 수 있습니다. 당신이 닫기 가능한 자체 유형의 특성이있는 경우 예를 들어, 그 특성은 그것을에서 혼합 할 수있는 유일한 일이의 닫기 가능한 인터페이스를 구현해야한다는 것을 알고있다.

    자체 유형은이 특성을 믹스 인 할 수 있습니다 어떤 종류를 지정할 수 있습니다. 당신이 닫기 가능한 자체 유형의 특성이있는 경우 예를 들어, 그 특성은 그것을에서 혼합 할 수있는 유일한 일이의 닫기 가능한 인터페이스를 구현해야한다는 것을 알고있다.

  10. ==============================

    10.업데이트 : 주체 차이가 자기 타입이 여러 클래스에 따라 달라질 수 있다는 점이다 (나는 조금 코너 케이스 것 인정). 예를 들어, 당신은 할 수 있습니다

    업데이트 : 주체 차이가 자기 타입이 여러 클래스에 따라 달라질 수 있다는 점이다 (나는 조금 코너 케이스 것 인정). 예를 들어, 당신은 할 수 있습니다

    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;
    }
    

    이것은 단지 사람과 비용의 서브 클래스입니다 아무것도 직원 믹스 인을 추가 할 수 있습니다. 비용은 한 사람 또는 그 반대를 확장하는 경우 물론,이 경우에만 의미가 있습니다. 요점은 자기 유형의 직원을 사용하는가에 따라 클래스의 계층 구조에 독립적이 될 수 있다는 것입니다. 그것은 무엇을 확장 무슨 상관하지 않는다 - 당신은 사람 대 비용의 계층 구조를 전환하면, 당신은 직원을 수정할 필요가 없습니다.

  11. ==============================

    11.첫 번째의 경우, B의 하위 특성 또는 서브 클래스 B는 추상적 인 특성이 될 수 있도록 어떤 A. 사용하여 혼합 될 수있다.

    첫 번째의 경우, B의 하위 특성 또는 서브 클래스 B는 추상적 인 특성이 될 수 있도록 어떤 A. 사용하여 혼합 될 수있다.

  12. from https://stackoverflow.com/questions/1990948/what-is-the-difference-between-self-types-and-trait-subclasses by cc-by-sa and MIT license