복붙노트

[SCALA] 어떻게 자기 형을 구현하는 등 스칼라의이 입력, 추상적 인 유형을 사용 하는가?

SCALA

어떻게 자기 형을 구현하는 등 스칼라의이 입력, 추상적 인 유형을 사용 하는가?

나는 다른 질문이에 대한 답을 찾을 수 없습니다. 나는 두 개의 서브 클래스, 1 콘크리트와 콘크리트 1과 추상 슈퍼 클래스 Abstract0을 가지고 있다고 가정하자. 내가 좋아하는 Abstract0 무언가를 정의 할 수있게하려면

def setOption(...): Self = {...}

어디 자체는 콘크리트 하위 유형이 될 것입니다. 이 같은에서 SetOption 호출 체인 허용합니다 :

val obj = new Concrete1.setOption(...).setOption(...)

여전히 OBJ의 추론 유형으로 1 콘크리트를 얻을.

내가 원하지 않는 것은이 정의하는 것입니다 :

abstract class Abstract0[T <: Abstract0[T]]

어렵게 클라이언트가이 유형을 처리하기 위해 그것을하게하기 때문이다. 나는 추상 형식 등 다양한 가능성을 시도했다 :

abstract class Abstract0 {
  type Self <: Abstract0
}

class Concrete1 extends Abstract0 {
  type Self = Concrete1
}

하지만 Abstract0이 유형 자체가 없기 때문에,에서 SetOption을 구현하는 것은 불가능합니다. 그리고이 사용 : 자기를 => 또한 Abstract0에 일을하지 않습니다.

이 문제에 어떤 해결책이 있습니까?

해결법

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

    1.이 this.type가 무엇인지입니다 :

    이 this.type가 무엇인지입니다 :

    scala> abstract class Abstract0 {
         |   def setOption(j: Int): this.type
         | }
    defined class Abstract0
    
    scala> class Concrete0 extends Abstract0 {
         |   var i: Int = 0
         |   def setOption(j: Int) = {i = j; this}
         | }
    defined class Concrete0
    
    scala> (new Concrete0).setOption(1).setOption(1)
    res72: Concrete0 = Concrete0@a50ea1
    

    당신이에서 SetOption 반환을 볼 수 있듯이 실제의 형태는, Abstract0하지 사용. Concrete0 다음 setOtherOption이 있다면 (새 Concrete0는) .setOption (1) .setOtherOption (...) 일 것

    업데이트 : 어떻게 새로운 인스턴스를 반환하는 주석 (에 JPP의 후속 질문에 대답하려면 : 질문에 설명 된 일반적인 접근은 (추상 유형을 사용하여) 권리입니다. 그러나 새 인스턴스 요구의 생성은 각 서브 클래스에 명시 될 수 있습니다.

    한 가지 방법은 다음과 같습니다

    abstract class Abstract0 {
      type Self <: Abstract0
    
      var i = 0
    
      def copy(i: Int) : Self
    
      def setOption(j: Int): Self = copy(j)
    }
    
    class Concrete0(i: Int) extends Abstract0 {
      type Self = Concrete0
      def copy(i: Int) = new Concrete0(i)
    }
    

    또 다른 하나는 스칼라의 수집 라이브러리에 사용되는 빌더 패턴을 따르는 것입니다. 즉에서 SetOption는 암시 빌더 파라미터를 수신한다. 이 새로운 인스턴스를 구축하는 것은 단지 '사본'보다 더 많은 방법으로 수행 할 수 있다는 장점을 가지고 복잡한 수행 할 수 있습니다 빌드 것을. 예를 들면 setSpecialOption은 반환 인스턴스가 SpecialConcrete해야 지정할 수 있습니다.

    여기에 솔루션의 그림입니다 :

    trait Abstract0Builder[To] {
        def setOption(j: Int)
        def result: To
    }
    
    trait CanBuildAbstract0[From, To] {
      def apply(from: From): Abstract0Builder[To]
    }
    
    
    abstract class Abstract0 {
      type Self <: Abstract0
    
      def self = this.asInstanceOf[Self]
    
      def setOption[To <: Abstract0](j: Int)(implicit cbf: CanBuildAbstract0[Self, To]): To = {
        val builder = cbf(self)
        builder.setOption(j)
        builder.result
      }
    
    }
    
    class Concrete0(i: Int) extends Abstract0 {
      type Self = Concrete0
    }
    
    object Concrete0 {
        implicit def cbf = new CanBuildAbstract0[Concrete0, Concrete0] {
            def apply(from: Concrete0) = new Abstract0Builder[Concrete0] {
               var i = 0
               def setOption(j: Int) = i = j
               def result = new Concrete0(i)
            }
        }
    }
    
    object Main {
        def main(args: Array[String]) {
        val c = new Concrete0(0).setOption(1)
        println("c is " + c.getClass)
        }
    }
    

    업데이트 2 : JPP의 두 번째 주석에 응답. 중첩 여러 수준의 경우, 형식 멤버 대신 형식 매개 변수를 사용하고 특성에 Abstract0합니다

    trait Abstract0[+Self <: Abstract0[_]] {
      // ...
    }
    
    class Concrete0 extends Abstract0[Concrete0] {
      // ....
    }
    
    class RefinedConcrete0 extends Concrete0 with Abstract0[RefinedConcrete0] {
     // ....
    }
    
  2. ==============================

    2.이 this.type의 정확한 사용 사례입니다. 그것은 같은 것이다 :

    이 this.type의 정확한 사용 사례입니다. 그것은 같은 것이다 :

    def setOption(...): this.type = { 
      // Do stuff ...
      this
    }
    
  3. from https://stackoverflow.com/questions/4313139/how-to-use-scalas-this-typing-abstract-types-etc-to-implement-a-self-type by cc-by-sa and MIT license