2010-07-07 45 views
9

Sto provando a scrivere una classe che ha una variabile membro generica ma che non è, di per sé, generica. Nello specifico, voglio dire che ho una lista di valori di "qualche tipo che implementa paragonabile a se stessa", in modo che io possa chiamare sort in quella lista ... Spero che abbia senso.Variabile di istanza generica nella classe non generica

Il risultato finale di ciò che sto tentando di fare è creare una classe tale che sia possibile creare un'istanza di detta classe con una matrice di (qualsiasi tipo specificato) e generare una rappresentazione di stringa per tale elenco. Nel codice vero e proprio, passo anche nella classe dei tipi che sto passando in:

String s = new MyClass(Integer.class, 1,2,3).asString(); 
assertEquals("1 or 2 or 3", s); 
String s = new MyClass(String.class, "c", "b", "a").asString(); 
assertEquals("\"a\" or \"b\" or \"c\"", s); 

Originariamente non ho nemmeno voglia di passare nella classe, volevo solo passare i valori e hanno il codice esamina la matrice risultante per scegliere la classe dei valori ... ma mi stava dando dei problemi.

Quanto segue è il codice che ho, ma non riesco a trovare il mojo corretto da inserire per il tipo di variabile.

public class MyClass { 
    // This doesn't work as T isn't defined 
    final List<T extends Comparable<? super T>> values; 

    public <T extends Comparable<? super T>> MyClass (T... values) { 
     this.values = new ArrayList<T>(); 
     for(T item : values) { 
      this.values.add(item); 
     } 
    } 

    public <T extends Comparable<? super T>> List<T> getSortedLst() { 
     Collections.sort(this.values); 
     return this.values; 
    } 
} 

errore sulla linea di dichiarazione variabili:

Syntax error on token "extends", , expected 

Qualsiasi aiuto sarebbe molto apprezzato.

Modifica: codice aggiornato da utilizzare Elenco anziché matrice, perché non sono sicuro che possa essere eseguito con gli array.

@ Marco: Da tutto quello che ho letto, ci tengo a dire "T è un tipo che è paragonabile a se stesso", non solo "T è un tipo che è paragonabile". Detto questo, il seguente codice non funziona neanche:

errore
public class MyClass { 
    // This doesn't work 
    final List<? extends Comparable> values; 

    public <T extends Comparable> MyClass (T... values) { 
     this.values = new ArrayList<T>(); 
     for(T item : values) { 
      this.values.add(item); 
     } 
    } 

    public <T extends Comparable> List<T> getSortedLst() { 
     Collections.sort(this.values); 
     return this.values; 
    } 
} 

on line aggiungere:

The method add(capture#2-of ? extends Comparable) in the type List<capture#2-of ? extends Comparable> is not applicable for the arguments (T) 

errore sulla linea sorta:

Type mismatch: cannot convert from List<capture#4-of ? extends Comparable> to List<T> 

Conclusione:

A quanto pare, Java non è in grado di gestire a voglio fare. Il problema è che quello che sto cercando di dire è:

Voglio un elenco di elementi che sono paragonabili contro se stessi, e io creare la lista in una sola volta dai dati passati in fase di creazione.

Tuttavia, Java vede che ho quella lista e non riesco a inchiodare giù che tutte le informazioni per la mia situazione è disponibile al momento della compilazione, da quando ho potuto provare ad aggiungere cose alla lista più tardi e, a causa di digitare cancellazione, non può garantire che la sicurezza. Non è davvero possibile comunicare a Java le condizioni coinvolte nella mia situazione senza applicare il tipo generico alla classe.

+3

Perché non è sufficiente avere un elenco di cose che implementano Comparable? –

+0

Non che risolva il problema, ma qualcosa che è paragonabile non significa che sia paragonabile a se stesso. Solo l'implementazione di Comparable non dice "è paragonabile a se stesso". – RHSeeger

+0

Puoi farlo con metodi statici. Vedi la mia seconda risposta. – TofuBeer

risposta

7

Penso che la semplice risposta sia che non puoi farlo. Se il tipo di uno degli attributi di una classe dipende da un parametro di tipo, tale parametro deve essere dichiarato a livello di classe.E non penso che abbia "senso" in nessun altro modo.

Se T nell'esempio non è un parametro di tipo della classe, che cos'è? Non può essere il parametro type del metodo, perché quel tipo è determinato da come viene chiamato il metodo. (Se il metodo viene chiamato in diversi contesti statici con diversi tipi desunti per T, qual è il tipo nozionale di T nel contesto della dichiarazione di attributo?)

Quindi, per portare questo indietro a quello che si sta cercando di fare qui , un'istanza di MyClass contiene elementi di qualche tipo e si desidera poter inserire e rimuovere elementi in modo staticamente sicuro. Ma allo stesso tempo non vuoi essere in grado di dire di che tipo si tratta. Quindi, come si suppone che il compilatore statico stia distinguendo tra un'istanza MyClass che contiene (ad esempio) gli oggetti Integer e uno che contiene gli oggetti String?

Non penso nemmeno che si possa implementare questo con typecheck dinamici espliciti. (Penso che la cancellazione di tipo significa che l'implementazione del metodo getSortedList() non riesce a scoprire che tipo reale è legato al suo tipo di ritorno.)

No. La vera soluzione è quella di rendere MyClass una classe generica che dichiara il parametro di tipo T ; per esempio.

public class MyClass <T extends Comparable<T>> { 

e rimuovere la dichiarazione del tipo di livello metodo parametro T dai due metodi.

+0

Sono d'accordo che T non ha alcun contesto in quel pezzo di codice, ma questo è il problema. Tutto quello che voglio fare è avere "un elenco di un tipo che possa essere confrontato con se stesso, in modo da poterlo ordinare". Cioè, apparentemente, più difficile di quanto possa sembrare in una classe che non è generica. – RHSeeger

+0

È solo difficile (in realtà impossibile) perché stai cercando di evitare di rendere generica la classe stessa. –

+0

Questo è solo, però. Concettualmente, la classe non è generica. Certo, posso rendere la classe generica per risolvere il problema, ma speravo ci fosse un modo per evitarlo perché è un odore di codice. Forse uno richiesto dalla lingua, ma un codice puzza comunque. – RHSeeger

1

considerarlo come questo (quello che sto per dire non è la realtà ma illustra il motivo per cui è necessario fare ciò che è necessario fare.):

class Foo<T> 
{ 
    private T value; 

    T getValue() { return value; } 
    void setValue(T val) {value = val; } 
} 

// some code that uses the above class 

Foo<Integer> iFoo = new Foo<Integer>(); 
Foo<String> sFoo = new Foo<String>(); 
iFoo.setValue(5); 
sFoo.setValue("Hello"); 

Quando questo accade il compilatore (non si ! VERAMENTE fare quello che sto per dire) genera il seguente codice:

class IntegerFoo 
{ 
    private Integer value; 

    Integer getValue() { return value; } 
    void setValue(Integer val) {value = val; } 
} 

class StringFoo 
{ 
    private String value; 

    String getValue() { return value; } 
    void setValue(String val) {value = val; } 
} 

// some code that uses the above class 

IntegerFoo iFoo = new IntegerFoo(); 
StringFoo< sFoo = new StringFoo(); 
iFoo.setValue(5); 
sFoo.setValue("Hello"); 

Se tu fossi in grado di avere le variabili di istanza/metodi parametrizzata senza parametrizzare la classe la cosa sopra (che non è la realtà) wouldn' lavoro.

Quello che stai cercando di fare dovrebbe essere possibile con metodi statici, ma non penso che sia quello che vuoi.

Puoi spiegare perché vuoi fare il codice che stai cercando di fare? Forse possiamo capire un modo migliore per fare ciò che vuoi fare che funziona nella lingua.

+0

Ho aggiunto un po 'più di dettagli su cosa sto cercando di fare. Il corto è che ho una variabile membro che è una lista che voglio ordinare, ma può essere una lista di ogni singolo (ordinabile) tipo. La classe stessa non è una classe "di quel tipo", di per sé .. solo quella variabile è. – RHSeeger

+0

Qualcosa di simile a MyClass (Integer.class, .........); non è una pratica rara. – TofuBeer

1

Credo che quanto segue otterrà ciò che desideri (digitando più forte di Comparable). Ciò impedirà alle persone di aggiungere oggetti comparabili che non sono dalla tua interfaccia alla lista e consentire più implementazioni.

public class test<T extends ComparableType> { 


final List<T> values = new ArrayList<T>(); 
    public test (T... values) { 
     for(T item : values) { 
      this.values.add(item); 
     } 
    } 

    public List<T> getSortedLst() { 
     Collections.sort(this.values); 
     return Collections.unmodifiableList(this.values); 
    } 
} 

public interface ComparableType extends Comparable<ComparableType> {} 

public class ConcreteComparableA implements ComparableType { 
    @Override 
    public int compareTo(ComparableType o) { 
    return 0; 
    } 
} 

public class ConcreteComparableB implements ComparableType { 
    @Override 
    public int compareTo(ComparableType o) { 
    return 0; 
    } 
} 

edit:

So che questo può essere evidente; ma se non si desidera la classe di essere generico questa soluzione funziona anche con:

public class test { 
    final List<ComparableType> values = new ArrayList<ComparableType>(); 

    public test (ComparableType... values) { 
     for(ComparableType item : values) { 
      this.values.add(item); 
     } 
    } 

    public List<ComparableType> getSortedLst() { 
     Collections.sort(this.values); 
     return Collections.unmodifiableList(this.values); 
    } 
} 
+0

Si finisce con una classe generica e si rimuove anche la possibilità di creare un'istanza con classi di sistema come String. – RHSeeger

+0

Modificato per indicare che funziona facilmente senza che la classe primaria sia Generica. Credo che l'obiettivo sia che solo i Comparables approvati potranno essere utilizzati (ad esempio, String non è utilizzabile dal design). – Syntax

2

C'è un sacco di avvertimenti incontrollati in questo, ma in linea di principio non è necessario per mantenere il List come tutt'altro che qualcosa contenente voi le cose lo so sono Comparable.Applica le regole necessarie al costruttore e tutto il resto dovrebbe andare bene. Che ne dite di qualcosa di simile:

public class MyClass { 

    final private List<Comparable> values; 

    public <T extends Comparable<? super T>>MyClass(T... values){ 
     this.values = new ArrayList<Comparable>(); 
     for(T item : values) { 
      this.values.add(item); 
     } 
    } 

    public <T extends Comparable<? super T>> List<T> getSortedLst() { 
     Collections.sort(this.values); 
     return (List<T>)this.values; 
    } 

} 

Un rapido test utilizzando la seguente mostra che per le classi che implementano comparabili (come Integer e String) MyClass si comporta come previsto, ma sarà gettare un errore di compilazione per le classi che non implementano Comparable :

class Junk { } 

    public static void main(String[] args){ 
     MyClass s = new MyClass(1,2,3); 
     System.out.println(s.getSortedLst()); 

     MyClass a = new MyClass("c", "a", "b"); 
     System.out.println(a.getSortedLst()); 

     MyClass c = new MyClass(new Junk()); 
    } 
+0

Questo è molto simile a quello che sto cercando.Detto questo, c'è un altro pezzo del puzzle (in qualche modo correlato, ma che lo rende più complesso di quanto voglio gestire nella ricerca) che mi morde quando vado da questa parte. Apprezzo la risposta, però. – RHSeeger

+0

@RHSeeger, dal momento che questo fondamentalmente risponde alla tua domanda apprezzerei un po 'più di informazioni sul perché non è sufficiente ... –

+0

il problema principale era che volevo davvero variabili membro che dicevano "un tipo che implementa paragonabile a se stesso", piuttosto che solo "un tipo che implementa comparabili", tanto per il codice di auto-documentazione quanto per altri pezzi di codice che vogliono un valore come input. Detto questo, odio anche vedere gli avvertimenti non controllati, il mio personale problema che è più con il design povero di Java che altro. – RHSeeger

-1
public class MyClass<T extends Comparable<? super T>> { 
    // This doesn't work as T isn't defined 
    final List<T> values; 

    public MyClass (T... values) { 
     this.values = new ArrayList<T>(Arrays.asList(values)); 
    } 

    public List<T> getSortedLst() { 
     Collections.sort(this.values); 
     return this.values; 
    } 
} 
1

lo farei in questo modo (l'ho fatto come un elenco o come un array), a meno che non si ha realmente bisogno la variabile di istanza/metodi:

import java.lang.reflect.Array; 
import java.util.ArrayList; 
import java.util.Arrays; 
import java.util.Collections; 
import java.util.List; 


public class MyClass 
{ 
    public static <T extends Comparable<T>> List<T> asSortedList(final T ... vals) 
    { 
     final List<T> temp; 

     temp = new ArrayList<T>(vals.length); 
     temp.addAll(Arrays.asList(vals)); 
     Collections.sort(temp); 

     return (Collections.unmodifiableList(temp)); 
    } 

    public static <T extends Comparable<T>> T[] asSortedArray(final Class<?> clazz, 
                   final T ... vals) 
    { 
     final T[] temp; 

     temp = (T[])Array.newInstance(clazz, 
           vals.length); 
     System.arraycopy(vals, 
         0, 
         temp, 
         0, 
         vals.length); 
     Arrays.sort(temp); 

     return (temp); 
    } 

    public static void main(final String[] argv) 
    { 
     final List<String> list; 
     final String[]  array; 

     list = MyClass2.asSortedList("c", "a", "b"); 
     System.out.println(list); 

     array = MyClass2.asSortedArray(String.class, "z", "y", "x"); 
     System.out.println(Arrays.deepToString(array)); 
    } 
} 
0

il tipo di vincolo che si desidera sulla variabile non può essere espresso direttamente. puoi introdurre un nuovo tipo per risolvere il problema.

static class MyList<T extends Comparable<? super T>> extends ArrayList<T>{} 

final MyList<?> values; 

tuttavia, non c'è motivo di essere estremamente sicuri in un codice privato. Generico è lì per aiutarti a chiarire i tuoi tipi, non per offuscarli.

Problemi correlati