Non sarebbe corretto. Ecco perché, con un esempio semplificato. Non abbiamo bisogno di due parametri generici, non abbiamo bisogno di sottotipi TI, TJ e TK sia
trait TA[X <: TA[X]]
trait TC[X <: TA[X]]
class Z extends TC[TA[_]]
argomenti di tipo [TA [_]] non sono conformi al di tratto parametro di tipo TC limiti [X < : TA [X]]
Vediamo perché questa dichiarazione non è corretta ed è corretto che non funzioni. Aggiungiamo un po 'di codice. Cambio TC
in un class
, in modo che il codice possa essere tradotto in java. A trait
farebbe altrettanto bene in scala.
trait TA[X <: TA[X]] {def f(x: X) }
class TC[X <: TA[X]] {def g(x: X) = x.f(x)}
Funziona bene, è anche x: X
TA[X]
, quindi non ha una routine f
, che accettare un X
.
Se cerchiamo invece
class TC2[X <: TA[_]] {def g(x: X) = x.f(x)}
poi fallisce. Sappiamo che esiste un metodo f
su x
, ma non sappiamo quale tipo sia necessario come argomento, non possiamo sapere che x
sarà ok. Infatti, supponiamo definiamo
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 }
Ora, se TC2
è stato permesso, ho potuto creare un TC2[T2]
, chiamare g
con una T2
, che rimetterebbe in f
T2
con un T2
. Questo non è permesso, e giustamente in quanto T2
non ha metodo t1Only
.
Questo spiega perché TC
non può essere accettato TA[_]]
come parametro, come che consentirebbe T2
, che non è compatibile con il metodo g
. E quindi perché non possiamo definire Z
con il parametro TA[_]
. Sarebbe lo stesso in java e con il tuo codice originale.
Edit: Mi sento un po 'in colpa per la mia risposta. Con la ragione che ho dato perché non dovrebbe essere permesso, ho pensato che ci sarebbe stata una soluzione semplice. Ha fallito, non ho avuto il tempo di indagare ulteriormente, e pubblicato senza menzionarlo. La soluzione alternativa era di tipo self. Se facciamo
trait TA[X <: TA[X]] {self: X => }
allora non possiamo definire T2 extends TA[T1]
. Quindi è più limitato del codice originale. Ma dobbiamo comunque accettare limitazioni al codice originale, perché non era corretto. Quindi non può essere solo un trucco di sintassi, deve rendere le cose impossibili che non lo sono. Pensavo che lo T2 extends TA[T1]
non fosse qualcosa che era previsto, e che era l'unica cosa da prevenire.
Apparentemente, non è stato, lo stesso errore. Ora non ho un esempio perché non dovrebbe funzionare. Che ovviamente non significa che non ce n'è.
Ho quindi esaminato la soluzione di Miles, chiedendomi perché la possibilità di T2 extends TA[T1]
non leda. Quindi, di nuovo, scartando TB
, Y
, TI
, TJ
, e TK
:
trait TA{type X}
trait TC{self => type X <: TA{type X <: self.X}
class Z extends TC{type X = TA}
Questo compila. E possiamo fare
trait T1 extends TA{type X = T1}
trait T2 extends TA{type X = T1}
ma c'è una cosa che non possiamo fare:
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)
}
Otteniamo il seguente errore su g
, per il x
argomento per f
tipo non corrispondente; trovati: x.type (con tipo sottostante TC.this.X) richiesto: x.x
TC
non è esattamente quella originale (come originariamente, g
era permesso). È così perché c'è uno type X <: self.X
su TA
mentre TC[X <: TA[X]]
era il più forte type X = self.X
. Se scriviamo che, invece, siamo tornati all'errore originale, Z
non viene compilato. Quindi questo TC
è un po 'tra lo TC
originale (type X = self.X
) e lo TC2
(nessuna conoscenza della X di TA). Di nuovo, una limitazione sul codice originale, non possiamo definire g
.
Se la limitazione è accettabile, tutto ok. Non so come scriverlo come un generico (né come scrivere il tipo self {self : X =>
con un membro di tipo astratto). Miles è sicuramente l'esperto, sono sicuro che sarebbe in grado di dire come è fatto o che non è possibile.
Mentre posso seguire il tuo argomento, penso che tu abbia risolto il problema cambiando il significato di Z. Z era un TK, e quindi un TC nel mio esempio, ma non era, e non doveva essere, un TI. Se TC è un "manager", TA è, ad esempio, un ufficio e TB qualcosa legato all'ufficio di cui un manager non si preoccupa, e TK è un gestore contabile e TI un ufficio contabile, quindi Z sarebbe un'istanza concreta di qualcosa che era allo stesso tempo un contabile e un ufficio contabile. Inoltre, voglio sottolineare che nel mio codice reale, TA e TB differirebbero perché definirebbero i propri comportamenti. –
Commento corretto: ho ottimizzato la definizione di Z sopra per affrontarla. Si noti che è possibile aggiungere qualsiasi altra definizione che si desidera TA, TB, TI, TJ ... semplicemente non è necessario ripetere i membri del tipo. –