Perché usare arg tipo 'oggetto di classe' invece di 'Paragonabile []'?

4

Nel codice sottostante, credo che sarebbe più appropriato rendere l'argomento del metodo di tipo Comparable[] anziché Object[] .

La prima ragione per cui sarebbe più appropriato è che si possa essere sicuri dalle eccezioni del runtime perché ogni oggetto passato deve implementare interface Comparable . Secondo, ogni oggetto in Java eredita da class Object in modo che uno possa digitare qualsiasi oggetto come (Object) per utilizzare i metodi di class Object .

package java.util; 
public class Arrays {
.........
public static void sort(Object[] a) {
        Object[] aux = (Object[])a.clone();
        mergeSort(aux, a, 0, a.length, 0);
    }
.........
}

C'è una ragione per cui il tipo Object[] è preferito rispetto al tipo Comparable[] come tipo di argomento?

    
posta overexchange 12.12.2014 - 06:53
fonte

3 risposte

3

Se osservi il codice mergeSort , vedrai che inserisce tutti i membri dell'array a in Comparable . Se qualche oggetto nell'array non è un Comparable , verrà generato un ClassCastException a meno che tu abbia creato un Comparator che sappia come confrontare i tuoi oggetti e passarli al metodo di ordinamento. Esempio delle due diverse tecniche (comparatore v comparabile) qui .

Gli sviluppatori Java hanno deciso che non dovresti essere forzato ad aggiungere un'interfaccia Comparable agli oggetti solo perché vuoi memorizzarli in un array e potresti scegliere di ordinarli a un certo punto. Questo ti dà una maggiore flessibilità (e meno cruft sulle tue classi) con il minor rischio di errori di runtime piuttosto che il controllo del tipo in fase di compilazione. La flessibilità è una vittoria, secondo me.

    
risposta data 12.12.2014 - 10:08
fonte
2

C'è un problema causato dal funzionamento degli array java, che è questo: ogni array ha il suo tipo, che non è necessariamente lo stesso tipo degli oggetti contenuti nella matrice, e il tipo non può essere modificato in fase di runtime.

Ad esempio:

Object [] a= new Object[]{
   (Integer)5,
   (Integer)2
};

Arrays.sort ((Comparable [])a);

Provocherebbe un'eccezione cast di classe, nonostante il fatto che tutti gli elementi in a siano comparabili.

Ora, si potrebbe sostenere che non dovremmo creare un array di oggetti, ma piuttosto un tipo più specifico, ma c'è un altro problema con questo. Considera questo codice:

class GenericArrayExample<T extends Comparable>
{ 
    T[] a;

    GenericArrayExample(int size)
    {
        a = new T[size];
    }
    public void sort ()
    {
        Arrays.sort (a);
    }
 }

Qual è il problema con questo, potresti chiedere: a è un array di Ts quindi dovrebbe essere comparabile. Ci sono tuttavia due problemi.

Il primo è che il costruttore non verrà compilato, perché per creare il nuovo array è necessario conoscere il tipo di T in fase di runtime, ma Java non può realmente fornire questa informazione (google java generics erasure se si desidera per scoprire perché). La solita soluzione è usare invece una serie di oggetti. Potresti invece usare Comparable [], ad eccezione di:

Non sarebbe bello se potessimo usare un'istanza di questa classe per archiviare anche oggetti non comparabili? Dovremmo evitare di usare il metodo di ordinamento, ovviamente, e dovrebbe generare qualche tipo di eccezione se proviamo ad usarlo ..

Questo può essere ottenuto solo se a è un array di oggetti, il che significa che Arrays.sort deve accettare matrici di oggetti.

    
risposta data 12.12.2014 - 10:26
fonte
2

Fondamentalmente, i tipi Java non si propagano sempre bene. Se in qualsiasi momento, passi a Object , perdi informazioni sui tuoi tipi (questo potrebbe non essere sempre vero in un blocco che il compilatore può analizzare). E non puoi sempre evitare Object , a causa di generici.

Dettagli

Se guardiamo Implementazione di OpenJDK , il commento dice:

  1. Gli oggetti devono implementare Comparable
  2. Gli oggetti devono essere comparabili tra loro: non puoi confrontare String e Integer istanze.

Entrambi i controlli possono essere eseguiti in fase di esecuzione (generando eccezioni se necessario). Ciò che suggerisci consente di eseguire il primo controllo durante la compilazione.

Tuttavia, dobbiamo considerare il metodo in relazione al suo utilizzo e, sfortunatamente, non puoi sempre conoscere il tipo di alcuni oggetti.

Originariamente ho scritto un esempio con List<String> e toArray , che restituisce un Object[] . La conversione in Object[] non consente di chiamare sort e non ho visto un modo semplice per convertire Object[] in String[] . Grazie ai commenti di SJuan76, ecco come farlo:

    String[] strings = names.toArray(new String[0]);

Quindi strings , grazie alla covarianza dell'array, è adatto per il confronto. Tuttavia, questo approccio non è favorevole allo sviluppo. Inoltre, non può essere distratto da costrutti linguistici: i generici usano tecniche di cancellazione del tipo che introducono istanze di Object . A proposito, dimentichiamoci dei generici per un momento: dopo tutto, le prime versioni di Java, per le quali è stato implementato sort , non hanno fornito i generici.

Come già detto, il tipo di codice scritto sopra non è certamente conveniente per l'uso. Ma ancora più importante, anche se è controllato in fase di compilazione, sono abbastanza sicuro che non è ottimizzato: c'è ancora un'operazione di copia in array in runtime: non si vuole scrivere un'API che impone questo tipo di costo a l'utente (considerare anche le prestazioni delle macchine virtuali originali).

Quindi, è meglio accettare semplicemente più parametri ed eseguire controlli in modo dinamico. Poiché, comunque, il punto (2) precedente richiede già verifiche di runtime e probabilmente genera eccezioni, potremmo anche accettare Object[] e tipi di test in fase di esecuzione.

Ora, potresti pensare che il fatto che alcune funzioni perdano informazioni di tipo come toArray (senza argomenti) è di per sé sfortunato, ma a questo punto è più un problema del sistema dei tipi di linguaggio.

    
risposta data 12.12.2014 - 10:02
fonte

Leggi altre domande sui tag