Ошибка компиляции сопоставления класса Case Scala с внутренними типами с псевдонимами?

StackOverflow https://stackoverflow.com/questions/1812695

  •  06-07-2019
  •  | 
  •  

Вопрос

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

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
}

Спасибо!

Это было полезно?

Решение

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

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 является ли служебный метод определенным как таковой.(он просто указывает тип своего аргумента с помощью довольно приятного вывода типа и небольшого использования Manifests)

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 в случае c и 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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top