2015-12-01 6 views
14

La specializzazione generica insieme ai tipi di valore sono una funzione prevista delle future JVM; link alla pagina del progetto Valhalla here.Specializzazione proiettata in Java 9 o successiva, vs Lista <int>: come funziona .remove()?

Ora, da quanto ho capito, sarebbe poi diventato possibile dichiarare un:

final List<int> myList = new ArrayList<>(); // for instance 

Ma poi List definisce un altro metodo .remove() oltre a quella definita nell'interfaccia Collection, che prende un int come un argomento che è l'indice nella lista da rimuovere; per questo motivo, attualmente, il contenuto di list nell'esempio seguente:

final List<Integer> list = new ArrayList<>(); 
list.add(1); 
list.add(2); 
list.add(3); 
list.remove(2); 

sarà [1, 2] e non [1, 3] (sovraccarico più specifico è scelto).

Tuttavia, se in futuro potremo dichiarare uno List<int>, avremo un problema: quale tipo di sovraccarico del metodo remove sarà scelto?

+1

questo non è previsto per Java 9, forse Java 10 o successivo –

+0

@SleimanJneidi Sono a conoscenza; ecco perché ho detto nell'argomento "Java 9 o successivo". – fge

+0

@ fge sto solo eliminando una possibilità :) non ce la faranno mai a 9 –

risposta

10

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.

+0

Bella scoperta ... dovrei leggere l'intero documento ... – fge