Questa risposta è basata su this paper di Brian Goetz, datata dicembre 2014. Questa è l'ultima che ho trovato sull'argomento; nota comunque che la carta è uno "schizzo informale" quindi non c'è ancora nulla di definitivo riguardo alla tua domanda.
primo luogo, un List<int>
non sarebbe un sottotipo di List<Integer>
(Subtyping):
Inizialmente, si potrebbe anche sembrare sensata che Box<int>
potrebbe essere un sottotipo di materie prime Box
. Tuttavia, data la nostra strategia di traduzione, la classe Box
non può essere una superclasse di qualunque classe rappresenti Box<int>
, in quanto quindi Box<int>
avrebbe un campo t
di tipo Object
, mentre t
dovrebbe essere di tipo int
. Quindi Box<int>
non può essere un sottotipo di Box
non elaborato. (E per la stessa ragione, Box<int>
non può essere un sottotipo di Box<Integer>
.)
...
Poiché i farmaci generici sono invarianti, non è sorprendente che List<int>
non è un sottotipo di List<Integer>
. La cosa leggermente sorprendente qui è che un tipo specializzato non può interagire con la sua controparte cruda. Tuttavia, questa non è una limitazione irragionevole; non solo i tipi non elaborati sono scoraggiati (essendo stati introdotti al solo scopo di supportare la graduale migrazione da codice non generico a codice generico), ma è ancora possibile scrivere codice generico usando metodi generici - vedi "Metodi generici".
Questo documento elenca anche "sfide di migrazione", e reference-primitive overloadings (il problema è la vostra domanda) è uno di loro:
Alcuni sovraccarichi che sono validi oggi sarebbe diventato problematico sotto specializzazione.Ad esempio, questi metodi avrebbero un problema se specializzata con T=int
:
public void remove(int position);
public void remove(T element);
Tali sovraccarichi sarebbe problematico sia sul lato specializzazione (quali metodi per generare) e sul lato di selezione sovraccarico (quale metodo per invocare.)
una proposta di soluzione si riferisce a come the "peeling" technique:
si consideri la coppia di sovraccarico in una classe List simile:
Gli usi esistenti di ListLike
riguarderanno tutte le istanze di riferimento, poiché quelle sono le uniche istanze attualmente consentite in un mondo di pre-specializzazione. Si noti che mentre la compatibilità richiede che le istanze di riferimento abbiano entrambi questi metodi, non richiede nulla di istanze non di riferimento (poiché attualmente non ne esistono).
L'intuizione dietro il peeling è osservare che, mentre siamo abituati a pensare a il ListLike
esistente come tipo generico, che potrebbe realmente essere l'unione di un tipo che è generico in tutte le istanze e un tipo che è generico attraverso solo le istanze di riferimento.
Se stavamo scrivendo questa classe da zero in un mondo post-specializzazione, potremmo avere scritto come:
interface ListLike<any T> {
void removeByIndex(int position);
void removeByValue(T element);
}
Ma, un tale cambiamento ora non sarebbe né fonte compatibile o compatibile con binario . Tuttavia, peeling ci permette di aggiungere questi metodi per il generico strato, e attuarli in uno strato specifico riferimento, senza richiedere loro nelle specializzazioni, ripristinando compatibilità:
interface ListLike<any T> {
// New methods added to the generic layer
void removeByValue(T element);
void removeByIndex(int pos);
layer<ref T> {
// Abstract methods that exist only in the ref layer
void remove(int pos);
void remove(T element);
// Default implementations of the new generic methods
default void removeByIndex(int pos) { remove(pos); }
default void removeByValue(T t) { remove(t); }
}
}
Ora, istanze di riferimento hanno remove(T)
e remove(int)
(nonché i nuovi metodi removeByIndex
e removeByValue
), garantendo la compatibilità e le specializzazioni presentano gli overload nonproblematic di removeByValue(T)
e removeByIndex(int)
. Le implementazioni esistenti di ListLike
continueranno a essere compilate poiché i nuovi metodi hanno un'implementazione predefinita per le specializzazioni di riferimento (che semplicemente collegano ai metodi di rimozione esistenti). Per le istanze di valore, removeByIndex
e removeByValue
sono considerate come astratte e devono essere fornite, ma la rimozione non esiste affatto.
Questa tecnica consente anche la tecnica "implementazione per parti"; è possibile dichiarare un abstract di metodo nel layer generico e fornire implementazioni concrete sia nel valore che nei livelli di riferimento. Se consentessimo i livelli per T=int
, abiliterebbe anche la tecnica di "specializzazione delle specializzazioni".
Con questa tecnica, compatibilità sarebbero mantenuti e nuovi metodi removeByValue
e removeByIndex
verrebbe utilizzato.
questo non è previsto per Java 9, forse Java 10 o successivo –
@SleimanJneidi Sono a conoscenza; ecco perché ho detto nell'argomento "Java 9 o successivo". – fge
@ fge sto solo eliminando una possibilità :) non ce la faranno mai a 9 –