Sería incorrecto. Aquí está el por qué, con un ejemplo simplificado. No necesitamos dos parámetros genéricos, que no necesitamos los subtipos de TI, TJ y los conocimientos tradicionales, ya sea
trait TA[X <: TA[X]]
trait TC[X <: TA[X]]
class Z extends TC[TA[_]]
tipo argumentos [TA [_]] no son conformes al rasgo parámetro de tipo de TC límites [X < : TA [X]]
Veamos por qué esta declaración no es correcta y es correcto que falle. Agreguemos algunos códigos. Cambio TC
a class
, para que el código se pueda traducir a java. A trait
le iría tan bien en scala.
trait TA[X <: TA[X]] {def f(x: X) }
class TC[X <: TA[X]] {def g(x: X) = x.f(x)}
Funciona bien, también es x: X
TA[X]
, por lo que no tiene una rutina f
, que aceptará una X
.
Si tratamos lugar
class TC2[X <: TA[_]] {def g(x: X) = x.f(x)}
continuación, se produce un error. Sabemos que hay un método f
en x
, pero no sabemos qué tipo se necesita como argumento, no podemos saber que x
estará bien. En efecto, supongamos que definimos
class T1 extends TA[T1]] {def f(t1: T1) = {}; def t1Only = println("only in T1")}
class T2 extends TA[T1]] {def f(t1: T1) = t1.t1Only }
Ahora bien, si se le permitió TC2
, podría crear un TC2[T2]
, llame g
con un T2
, lo que exigiría f
en T2
con un T2
. Esto no está permitido, y con razón, así como T2
no tiene el método t1Only
.
Esto muestra por qué no puede ser TC
acepta TA[_]]
como parámetro, ya que ello permitirá T2
, que no es compatible con el método de g
. Entonces, ¿por qué no podemos definir Z
con el parámetro TA[_]
? Sería exactamente el mismo en Java, y con su código original también.
Editar: Me siento un poco culpable por mi respuesta. Con la razón por la que di por qué no debería permitirse, pensé que habría una solución simple. Falló, no tuve tiempo de investigar más y lo publiqué sin mencionarlo. La solución fue un tipo de uno mismo. Si hacemos
trait TA[X <: TA[X]] {self: X => }
entonces no podemos definir T2 extends TA[T1]
. Por lo tanto, es más limitado que el código original. Pero tenemos que aceptar limitaciones al código original de todos modos, porque no era correcto. Entonces no puede ser solo un truco de sintaxis, tiene que hacer cosas imposibles que no lo fueron. Pensé que T2 extends TA[T1]
probablemente no era algo intencional, y que era lo único que debía evitar.
Al parecer, no fue, el mismo error. Ahora no tengo un ejemplo de por qué no debería funcionar. Lo que por supuesto no significa que no hay ninguno.
Luego eché un vistazo a la solución de Miles, preguntándome por qué la posibilidad de T2 extends TA[T1]
no le hace daño. Así que de nuevo, descartando TB
, Y
, TI
, TJ
y TK
:
trait TA{type X}
trait TC{self => type X <: TA{type X <: self.X}
class Z extends TC{type X = TA}
Esto compila. Y podemos hacer
trait T1 extends TA{type X = T1}
trait T2 extends TA{type X = T1}
Pero hay una cosa que no podemos hacer:
trait TA {type X <: TA; def f(x: X)}
trait TC {self =>
type X <: TA{type X <: self.X}
def g(x: X) = x.f(x)
}
Obtenemos el siguiente error en g
, para la x
argumento para f
coincidencia de tipos; encontrado: x.type (con tipo subyacente TC.this.X) requerido: x.x
TC
no es exactamente el original (como originalmente, g
se dejó). Es así porque hay un type X <: self.X
en TA
mientras que TC[X <: TA[X]]
fue el más fuerte type X = self.X
. Si escribimos eso, volvemos al error original, Z
no compila. Por lo tanto, este TC
es algo entre el original TC
(type X = self.X
) y TC2
(sin conocimiento de la X de TA). De nuevo, una limitación en el código original, no podemos definir g
.
Si la limitación es aceptable, está bien. No sé cómo escribirlo como genérico (ni cómo escribir el tipo de uno mismo {self : X =>
con un miembro de tipo abstracto). Miles es definitivamente el experto, estoy seguro de que podría decir cómo se hace o que no es posible.
Si bien puedo seguir su argumento, creo que ha solucionado el problema cambiando el significado de Z. Z era un TK, y por lo tanto un TC en mi ejemplo, pero no era, y no debería ser, una TI. Si TC es un "gerente", TA es, por ejemplo, una oficina y TB algo relacionado con la oficina que a un gerente no le importa, y TK es un gerente de contabilidad y TI una oficina contable, entonces Z sería una instancia concreta de algo que era al mismo tiempo un gerente de contabilidad y una oficina de contabilidad. Además, quiero señalar que en mi código real, TA y TB diferirían porque definirían sus propios comportamientos. –
Comentario equitativo: He modificado la definición de Z para abordarlo. Tenga en cuenta que puede agregar cualquier otra definición que desee a TA, TB, TI, TJ ... simplemente no necesita repetir el tipo de miembros. –