¿Error de compilación de clase de caso Scala con tipos internos con alias?
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!
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