Право ... Внутренние классы в Скале немного неудобны. Попробуем простой пример, прежде чем я покажу вам переписанную версию кода, который вы предоставили.
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
Нет удовольствия. Я едва начал отвечать на него! :-) –