2009-11-28 4 views
4

Как использовать сопоставление классов case с псевдонимами? Это работает, когда я вытаскиваю CB из контейнера.Scala Case подходит для компиляции ошибки с внутренними внутренними типами?

class DoStuff[TKey](
    val c : Container[TKey]#CB 
) 
{ 
    type CB = Container[TKey]#CB 
    type C1 = Container[TKey]#C1 
    type C2 = Container[TKey]#C2 

    c match { 
    case C1(e1) => e1 // - not found: value e1 - not found: value C1 
    case C2(e2) => e2 // - not found: value e2 - not found: value C2 
    } 
} 

trait Container[TKey] 
{ 
    abstract trait CB 
    case class C1(val e : AnyRef) extends CB 
    case class C2(val e : AnyRef) extends CB 
} 

Спасибо!

ответ

8

Право ... Внутренние классы в Скале немного неудобны. Попробуем простой пример, прежде чем я покажу вам переписанную версию кода, который вы предоставили.

case class Foo(x: Int) { 
    case class Bar(y: String) 
} 

Теперь рассмотрим следующий фрагмент кода:

val x = new Foo(1) 
val y = new Foo(2) 

val a = new x.Bar("one") 
val b = new y.Bar("two") 

Наиболее общий тип a и b является Foo#Bar, что означает внутренний класс Bar с любым наружным объектом типа Foo. Но мы могли бы быть более конкретными, говоря, что тип a является x.Bar и типа b является y.Bar - это означает, что a является экземпляром внутреннего класса Bar с внешним объектом x, похожим на b.

Фактически вы можете видеть, что типы различны, вызывая typeOf(a) и typeOf(b), где typeOf - это метод полезности, определенный как таковой. (Это просто дает тип аргумента по довольно хорошему умозаключению типа и немного использования Manifest с)

def typeOf[T](x: T)(implicit m: scala.reflect.Manifest[T]) = m.toString 

Как внутренний объект содержит ссылку на его объект ограждающего, вы не экземпляра внутреннего объекта без какого-либо указания его внешнего объекта. Поэтому вы можете позвонить new x.Bar("one"), но вы не можете позвонить new Foo#Bar("?") - как во втором случае вы не указали, каков внутренний объект для нового объекта, который вы пытаетесь построить.

Итак, давайте вернемся к фрагменту кода. Когда вы сопоставляете шаблоны, вы на самом деле вызываете конструктор - при вызове C1(e1). Поскольку C1 является псевдонимом для Container[TKey]#C1 , вы попытались вызвать конструктор внутреннего класса без указания его внешнего объекта, который не работает по причинам, указанным выше. То, как я хотел бы написать код будет выглядеть следующим образом:

trait Container[TKey] { 
    abstract trait CB 
    case class C1(val e : AnyRef) extends CB 
    case class C2(val e : AnyRef) extends CB 
} 

class DoStuff[TKey] (val c: Container[TKey], val element: Container[TKey]#CB) { 
    element match { 
    case c.C1(e1) => Some(e1) 
    case c.C2(e2) => Some(e2) 
    case _  => None 
    } 
}

Теперь это компилирует и, надеюсь, он делает то, что вы хотите. Но принимайте это с большой осторожностью! Из-за стирания типа Scala не может гарантировать, что element действительно имеет тип c.CB или тип d.CB, где CB в случае и d оказались такими же.

Рассмотрим следующий пример:

def matcher(arg: Foo#Bar) = { 
    arg match { 
    case x.Bar(n) => println("x"); 
    case y.Bar(n) => println("y"); 
    } 
} 

где x и y являются такими, как раньше. Попробуйте выполнить следующие функции:

matcher(a) 
matcher(b) 

Они оба печатают x!

Поэтому я хотел бы переписать код, чтобы явно есть элемент в контейнере:

trait Container[TKey] { 
    abstract trait CB 
    case class C1(val e : AnyRef) extends CB 
    case class C2(val e : AnyRef) extends CB 
    val element: CB 
} 

class DoStuff[TKey](val c: Container[TKey]) { 
    c.element match { 
    case c.C1(e1) => Some(e1) 
    case c.C2(e2) => Some(e2) 
    case _  => None 
    } 
}

Надеется, что это помогает :)

- Flaviu Cipcigan

+0

Нет удовольствия. Я едва начал отвечать на него! :-) –

Смежные вопросы