2017-01-24 8 views
4

파생 클래스는 매개 변수

내가 대략 다음과 같은 지금,하는 클래스 계층 구조를 가지고 [아래 수정 됨] :

sealed trait Foo { 
    def method: Any 
} 

case class Bar extends Foo { 
    def method: Array[String] = // implementation 
} 

case class Baz extends Foo { 
    def method: Map[String, Array[String]] = // implementation 
} 

나는 추상적 인 방법을 Any의 반환 유형을주고있다을 왜냐하면 사례 클래스의 반환 유형은 반드시 다르지만 비슷한 목적을 공유하기 때문입니다. 이런 이유로, 나는 공통적 인 행동을 모델로 삼으면서 특성을 유지하고 싶습니다. 그리고 이것이 이것이 컴파일되도록 만드는 유일한 방법입니다. 이것은 스칼라 타입 시스템의 정신에 위배된다는 것을 알기 때문에 아래 첫 번째 질문을합니다.

그런 다음, 다른 클래스는 생성자 매개 변수로 Foo의 서브 클래스는 기대하고, 나는 다음이 아닌 다른이, 표시하기 위해 방법을 모른다 : 기대

나중에 클래스
class Qux(foo: Foo) { 
    val m = foo.method 
    ... 
    ... 
} 

Qux있다 방법을 발 mFoo의 특정 서브 클래스 (Bar 또는 Baz)에 해당하는 유형이어야합니다,하지만 난

... type mismatch; 
[error] found : Any 
[error] required: Array[String] 
같은 컴파일 오류를 얻고있다

  • 나는 이것이 내 특정 문제를 표현하는 올바른 방법이라고 생각하기에 충분 스칼라 익숙하지만, 그것에 대해 이동하는 방법을 알고 충분히 익숙하지 :

    그래서 나는 몇 가지 질문이 있습니다. 내가하려는 일을하는 적절한 방법은 무엇입니까?

  • 또한
  • , mFoo에서 추상적 인 방법을 Bar 또는 Baz에서 특정 method에 의해 반환되는 값으로 처리, 그리고해야 Qux 클래스 말할 수있는 방법은 무엇입니까?

편집 : (추상 형식의 멤버를 사용) @marios에 의해 제안 된 접근 방식을 복용은 올바른 방향으로 단계가 될 것 같다,하지만 형식이 일치 이제 나타납니다. 클래스 Qux 내에서, 지금

class Qux[X <: Foo](sc: SparkContext, val foo: X) { 
    val m: foo.A = foo.method 

    def process(rows: DataFrame) = foo match { 
    case Bar(sc, _) => BarProcessor(sc, m).doStuff(rows) 
    case Baz(sc, _) => BazProcessor(sc, m.keys).doStuff(rows, m.values) 
    } 
} 
예를 들어 BarProcessor가 인스턴스화되고

, Array[String]BazProcessor이 물건을 할 Bazmethod에 의해 반환 된 값에서 키 - 값 쌍을 필요로한다. 나는 foo이 (value keys is not a member of Qux.this.foo.A의 라인을 따라, 등) BazmMap - 특정 메서드를 호출 할 때 그러나, 지금

[error] Qux.scala:4: type mismatch; 
[error] found : Qux.this.foo.A 
[error] required: Array[String] 
[error]  case Bar(sc, _) => BarProcessor(sc, m).doStuff(rows) 
[error]          ^

비슷한 오류와 같은 오류를 얻고있다가 표시됩니다. m이 아니며 실제로이고 Array[String] 인 것으로 알고 있습니다. 유형은 A입니다. 그러나 스칼라에게 이것을 원하는 타입으로 "번역"한다고 말하는 방법이 있을까요?

+1

는 "경계"에 의해 제한 될 수있다 "형식 매개 변수를"(반환 유형을보고). –

답변

4

당신 ADT의 개별 유형에 액세스 할 수있는 쉬운 방법 것은 제네릭 형식 매개 변수 대신 추상 형식의 멤버을 사용하는 것입니다.

sealed trait Foo { 
    type A 
    def method: A 
} 

case object Bar extends Foo { 
    type A = Array[String] 
    def method: A = Array.empty[String] 
} 

case object Baz extends Foo { 
    type A = Map[String, Array[String]] 
    def method: A = Map.empty[String, Array[String]] 
} 

case class Qux[X <: Foo](foo: X) { 
    def m: X#A = foo.method 

    // You can then pattern match on m 
    def f = m match { 
    case a: Baz.A => a.size // Use Baz#A if Baz is a class and not an object 
    case b: Bar.A => b.size // Use Bar#A if Bar is a class and not an object 
    } 
} 

를 사용

@ Qux(Baz).m 
res6: Map[String, Array[String]] = Map() 

@ Qux(Bar).m 
res7: Array[String] = Array() 
+0

이것은 매우 유망한 접근 방법 인 것으로 보인다. 고맙습니다! 그러나 저는 컴파일러가 "타입 A가 타입 인자 X의 멤버가 아닙니다"라는 불만을 Qux의 정의에서 가지고 있습니다. – user4601931

+0

흠 ... 어떤 버전을 사용하고 있습니까? 나는 이것을 2.11.8에서 시도했다. 방금 또 다른 기회를주었습니다. – marios

+0

쉘에': paste'를 사용하고 전체 코드를 복사하십시오. 그게 너에게 효과가 있니? – marios

3

당신이처럼 특성에 유형 매개 변수를 추가 할 수 있습니다

sealed trait Foo[A] { 
    def method: A 
} 

case class Bar extends Foo[Array[String]] { 
    def method: Array[String] 
} 

case class Baz extends Foo[Map[String, Array[String]]] { 
    def method: Map[String, Array[String]] 
} 
+0

이렇게하는 것이 가장 간단한 방법입니다! 좋은 대답. 그러나 질문의 ​​두 번째 부분을 실제로 다루고 있는지 확실하지 않습니다. Qux는 어떻게 작동합니까? – marios

+0

해당 클래스에도 유형 클래스 변수를 추가하십시오. – Tyler

+0

에 관심이 없다면'클래스 Qux [A]', 또는'클래스 Qux [_]'와 같은 것입니다. 그러나 그는 타입을 신경 쓰고 있습니다. 'val m = foo.method' OP는 m이 Foo [A]'의 같은 타입 A가되기를 원합니다. 'class Qux [A] (foo : Foo [A])와 같은 것이 트릭을해야한다고 생각합니다. – marios

관련 문제