Implementazione di un elenco generico in Java

1

Nel tentativo di comprendere Generics in Java (e come usarli), sto provando a implementare una struttura di lista che li usa ma hanno colpito un muro.

Scenario:

Supponiamo che ci siano alcune classi (le chiameremo ClassA, ClassB e ClassC) che estendono tutti ClassBase. ClassBase è astratto e contiene semplicemente un codice ID con getter e setter appropriati. Le sottoclassi aggiungono attributi aggiuntivi, ma questi non sono importanti.

L'elenco è progettato per essere in grado di contenere un numero delle sottoclassi di cui sopra e manipolare l'elenco utilizzando l'attributo ID comune. L'elenco deve essere disponibile e ogni elenco conterrà solo un tipo di sottoclasse (quindi un elenco di ClassA, ClassB o ClassC. Nessuna combinazione mista di questi).

Tentativo 1:

Ho impostato la mia classe List in questo modo:

ClassList<T extends ClassBase>

Quindi dichiaro un array di elementi come "private T list []" e scrivo il resto della classe. Le firme per i getter e setter di elementi sono le seguenti, poiché potrebbero essere importanti:

public T get(int index)
public void set(int index, T item)

Tutto sembra essere buono, tranne quando vado a compilare si lamenta di ogni istanza in cui provo a ricreare l'array sottostante (per ridurlo / ingrandirlo).

.\test\ListClass.java:10: error: generic array creation
        list = new T[0];
               ^

Quindi dopo un po 'di riflessione e lettura l'errore ha senso, dato che il compilatore non sa quali costruttori saranno disponibili (almeno questo è il modo in cui lo capisco), quindi non sembra che in questo modo funzionerà.

Tentativo 2:

Conservo la stessa dichiarazione di classe, ma cambio l'array interno in "private ClassBase list []", e quindi adattare il mio codice di conseguenza. La modifica principale consiste nel trasmettere da ClassBase a T e viceversa quando necessario. Quindi il getter ad esempio si presenta così:

public T get(int index)
{
    return (T)(list[index]);
}

Quindi ora il mio codice viene compilato ed eseguito, ma ogni istanza in cui ho eseguito il casting sopra riportato genera ora l'infame avviso non controllato:

.\test\ListClass.java:16: warning: [unchecked] unchecked cast
        return (T)(list[index]);
                  ^

Riesco a capire perché l'avvertimento appare, ma allo stesso tempo ListClass controlla tutti i mezzi per accedere e manipolare l'elenco, quindi posso garantire che solo gli elementi di tipo T siano posizionati lì.

Problema:

Suppongo di poter sopprimere gli avvertimenti. Ma sono convinto che siano lì per una ragione e preferirei sbarazzarci di loro nel modo giusto. Il problema è che non riesco a capire quale sia il modo "corretto" (o se in realtà il sopprimere l'errore è il modo corretto).

Questa è la mia domanda. Cosa c'è di sbagliato nel mio design e come lo risolvo?

Grazie in anticipo.

Nota:

Prima che qualcuno mi dica di usare uno dei generici esistenti e di smettere di reinventare la ruota, non è questo il punto. Il mio obiettivo non è quello di ricreare un sostituto per le liste generiche, è quello di capire i generici, e una lista come sopra riguarda il miglior esempio che riesco a pensare e che mostra chiaramente come funzionano.

    
posta user3404036 29.07.2015 - 12:06
fonte

3 risposte

0

L'avviso cast non controllato è uno degli avvertimenti più problematici, fastidiosi e, in definitiva, inutili emessi da java. Dico "in definitiva inutile" invece che semplicemente "inutile" perché non è del tutto privo di merito, è bello sapere che hai una conversione incerta da qualche parte, ma si scopre che se fai qualsiasi lavoro con i generici in java, vorrai aggiungere @SuppressWarnings("unchecked") come macro alla tua tastiera, è la frequenza con cui dovrai sopprimere questo avviso.

In sostanza, il valore dell'avviso non controllato è che tutte le conversioni iffy saranno facilmente identificabili nel codice sorgente a causa delle dichiarazioni @SuppressWarnings("unchecked") ad esse associate.

Quindi, non c'è nulla di sbagliato nell'eliminare gli avvertimenti non controllati, purché tu sappia cosa stai facendo. Se in seguito si verifica un'eccezione di classe in fase di esecuzione, saprai che la causa del problema è probabilmente nell'istruzione @SuppressWarnings("unchecked") più vicina.

Assicurati solo quando possibile di sopprimere gli avvertimenti non controllati a livello di istruzione, anziché a livello di funzione o a livello di classe. Per qualche strana ragione, java non permetterà questo:

@SuppressWarnings("unchecked")
return (T)x;

quindi questo potrebbe indurti a sopprimere l'avvertimento a livello di funzione e farlo con esso. Tuttavia, funzionerà:

@SuppressWarnings("unchecked")
T temp = (T)x;
return temp;

In questo modo, l'avviso è localizzato sull'esatta dichiarazione che contiene la conversione iffy, quindi sai esattamente dove si trova ogni singola conversione iffy. Se sopprimi la funzione o a livello di classe, allora se le conversioni potrebbero essere ovunque, non sapresti.

Detto questo, un'altra buona opzione che hai è di abbandonare l'idea di creare e manipolare il tuo array di oggetti generici, e avere la tua classe di listino in-house con ArrayList<T> di Java. Questa classe esiste proprio per incapsulare array generici, e come Michael Borgwardt ha già dichiarato , se guardi al suo codice sorgente, vedrai che contiene un array di java.lang.Object e che è pieno di clausole @SuppressWarnings("unchecked") . Il vantaggio di utilizzarlo è che è stato accuratamente testato, quindi tutte queste conversioni sono conosciute per essere buone, quindi non puoi sbagliarti.

    
risposta data 29.07.2015 - 13:57
fonte
0

L'unica soluzione funzionante è sopprimere gli avvertimenti. Questo è anche ciò che fanno le collezioni generiche esistenti.

Quindi non significa che ci sia qualcosa di sbagliato nel tuo progetto - se c'è qualcosa di sbagliato, è con la progettazione di generici Java (che dovevano scendere a compromessi per ottenere una compatibilità verso il basso).

    
risposta data 29.07.2015 - 12:31
fonte
0

È una specie di cattura 22, sono d'accordo. Tuttavia, se sai che solo le istanze di tipo T vengono aggiunte all'elenco, puoi sopprimere l'avviso senza ripercussioni. Il compilatore non è abbastanza intelligente da ricavare queste informazioni da solo.

Tuttavia, sappi anche che dovresti creare un altro metodo che permetta l'inserimento di istanze di tipo ClassBase , il compilatore ti permetterà di farlo, e l'avviso che hai soppresso ti avviserà esattamente di questo tipo di problema e renderà un ClassCastException per i tipi che non sono T , quindi tieni presente questo fatto!

    
risposta data 29.07.2015 - 12:32
fonte

Leggi altre domande sui tag