Scala Caso erro de compilação correspondente classe com tipos internos alias?

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

  •  06-07-2019
  •  | 
  •  

Pergunta

Como faço para usar caso classe correspondência com os tipos de alias? Isso funciona quando eu puxo CB etc fora do recipiente.

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
}

Obrigado!

Foi útil?

Solução

Right ... Classes internas em Scala são um pouco complicadas. Vamos tentar um exemplo simples antes de eu mostrar-lhe a versão reescrita do código que você forneceu.

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

Agora, considere o seguinte trecho de código:

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

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

O tipo mais genérico de a e b é Foo#Bar, o que significa que o Bar classe interna com qualquer objecto exterior de tipo Foo. Mas poderíamos ser mais específico em dizer que o tipo de a é x.Bar eo tipo de b é y.Bar -. O que significa que a é uma instância da Bar classe interna com o x objeto externo, similar para b

Você pode realmente ver que os tipos são diferentes, chamando typeOf(a) e typeOf(b), onde typeOf é um método utilitário definido como tal. (Ele só dá o tipo de seu argumento por tipo bastante agradável inferência e um pouco de uso de Manifests)

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

Como um objeto interno contém uma referência ao seu objeto anexando, você não pode instanciar um objeto interno sem alguma forma caracterizando seu objeto exterior. Portanto, você pode chamar new x.Bar("one") mas você não pode chamar new Foo#Bar("?") -. Como no segundo caso você não tenha especificado qual é o objeto interno para o novo objeto tentar construção

Então, vamos retornar ao seu trecho de código. Quando você está padrão de correspondência, na verdade você está chamando um construtor - ao chamar C1(e1). Como C1 é um alias para Container[TKey]#C1 você tentou chamar um construtor de uma classe interna sem especificar seu objeto exterior, que falhar devido a razões acima expostas. A maneira que eu iria escrever o código seria da seguinte forma:

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
  }
}

Agora Isso compila e espero que ele faz o que quiser. Mas levar isso com muito cuidado! Devido ao tipo de apagamento, Scala não pode garantir que o element é realmente do tipo c.CB ou do tipo d.CB onde o CB no caso de c e d acontecer a ser o mesmo.

Veja este exemplo:

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

onde x e y são como antes. Tente executar o seguinte:

matcher(a)
matcher(b) 

Ambos x impressão!

Portanto, eu iria reescrever o código para ter explicitamente um elemento no recipiente:

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
  }
}

Espero que ajude:)

- Flaviu Cipcigan

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top