Pregunta

¿Cómo uso la coincidencia de clase de caso con tipos con alias? Esto funciona cuando saco CB, etc. del contenedor.

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
}

¡Gracias!

¿Fue útil?

Solución

Correcto ... Las clases internas en Scala son un poco complicadas. Probemos con un ejemplo simple antes de mostrarle la versión reescrita del código que ha proporcionado.

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

Ahora, considere el siguiente fragmento 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") 

El tipo más genérico de a y b es Foo # Bar , lo que significa la clase interna Bar con cualquier objeto externo de tipo Foo . Pero podríamos ser más específicos al decir que el tipo de a es x.Bar y el tipo de b es y.Bar - lo que significa que a es una instancia de la clase interna Bar con el objeto externo x , similar para b .

Puede ver que los tipos son diferentes llamando a typeOf (a) y typeOf (b) , donde typeOf es una utilidad método definido como tal. (solo da el tipo de su argumento mediante una inferencia de tipos bastante agradable y un poco de uso de Manifest s)

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

Como un objeto interno contiene una referencia a su objeto envolvente, usted no puede crear una instancia de un objeto interno sin especificar de alguna manera su objeto externo. Por lo tanto, puede llamar a new x.Bar (" one ") pero no puede llamar a new Foo # Bar ("? & Quot;) , como en el segundo caso no ha especificado cuál es el objeto interno para el nuevo objeto que intenta construir.

Entonces, regresemos a su fragmento de código. Cuando está haciendo coincidir un patrón, en realidad está llamando a un constructor, al llamar a C1 (e1) . Como C1 es un alias para Container [TKey] # C1 ha intentado llamar a un constructor de una clase interna sin especificar su objeto externo, que falla debido a los motivos descritos anteriormente. La forma en que escribiría el código sería la siguiente:

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

Ahora esto se compila y es de esperar que haga lo que quieras. ¡Pero toma esto con mucho cuidado! Debido a la eliminación de tipo, Scala no puede garantizar que el elemento sea realmente del tipo c.CB o del tipo d.CB donde el CB en el caso de c y d son los mismos.

Considere este ejemplo:

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

donde x y y son como antes. Intenta ejecutar lo siguiente:

matcher(a)
matcher(b) 

¡Ambos imprimen x !

Por lo tanto, reescribiría el código para tener explícitamente un elemento en el contenedor:

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 ayude :)

- Flaviu Cipcigan

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top