generici Java e modelli di progettazione: non parametrizzazione di un riferimento a un tipo generico è sempre una cosa negativa?

StackOverflow https://stackoverflow.com/questions/7327209

Domanda

a questa domanda è in parte legato alla mia ultima domanda .

Ho una classe generica che rappresenta un insieme di oggetti generici:

public interface MyObject<N extends Number>{}

public interface MyCollecion<N extends Number> {
    public foo(MyObject<N> obj)
}

Nel mio disegno queste collezioni di oggetti sono costruiti da una classe cliente (chiamiamolo CollectionBuilder) attraverso un approccio classe astratta:

public interface AbstractCollectionFactory {
    public abstract <N extends Number> MyCollection<MyObject<N>> createCollection(String collectionType);

}

collezioni generici dovrebbe essere costruito in questo modo:

public class CollectionBuilder{
    AbstractCollectionFactory f;
    public void buildDoubleCollection(){

         MyCollection<MyObject<Double>> c = f.<Double>createCell("MyType");
    }
    public void buildIntegerCollection(){...}
}

Ok.Since qui tutto ok. CollectionBuilder è a conoscenza di quello che è il tipo concreto generica per specificare (doppia in questo caso) e può costruire in modo corretto. Posso compilare questo senza alcun preavviso e tutto dovrebbe funzionare bene.

Ora ho una domanda relativa sia ai farmaci generici e il design del mio programma.

Ho un'altra classe nella mia applicazione che necessità di utilizzare la collezione costruita da CollectionBuilder (chiamiamolo questa classe UserClass). L'UserClass:

  1. non ha bisogno di sapere di quale particolare tipo di cemento sono la sua collezione (MyCollection<MyObject<Double>> o MyCollection<MyObject<Integer>>).
  2. eseguire alcune operazioni su questi insiemi invocando alcuni metodi definiti nell'interfaccia MyCollection.
  3. Non vorrei aggiungere un tipo generico ad esso.

Nella situazione descritta, è che una cattiva idea non parametrizzare il MyCollection classe generica insied UserClass?

public UserClass{
     MyCollection a;  //warning 
     MyCollection<?> b; //no warning

     public method(MyObject o){  //warning
          a.foo(b); //compile       
     }
     public method2(MyObject<?> o){
          b.foo(o); //error do not compile
     }
}
compilatore

Java protesta sempre con un avvertimento se non specificare il parametro generico. In ogni caso dall'interno UserClass non so il parametro concreto (e vorrei non lo sanno) e anche dichiarandolo con "?" non mi consenta di richiamare il metodo foo sul MyCollection.

Quindi la domanda è:

  1. non parametrizzazione di un riferimento a un tipo generico è sempre una cosa negativa?
  2. se la risposta a 1 è NO, questo è un situazione giusta per non farlo?
  3. se la risposta a 1 è SI, come posso utilizzare il metodo MyCollection dall'interno UserClass senza conoscere i loro tipi generici?
  4. è mio un disegno male?

Spero di essere stato chiaro. Ho lottare su questo problema da giorni, e non ho idea. Ti prego, aiutami.

È stato utile?

Soluzione

  1. Secondo il compilatore Java, sì. E questa risposta contiene un sacco di punti buoni per spiegare perché. Personalmente sono d'accordo in alcune situazioni, soprattutto dove l'uso di Class è interessato (come, per esempio, se avete un Map<Class, Object>). In questi casi dover sempre virare <?> al fine di "Class" si sente proprio come tedio inutile, e non rende il codice più leggibile.

    L'idea di avere Class essere parametrizzato in base al tipo di oggetto è associato ha sempre sentito un po 'dubbioso, in primo luogo, per me. Il punto di Class è di avere un'interfaccia comune che può essere utilizzato per ottenere informazioni su qualsiasi oggetto (ala riflessione), indipendentemente dal suo tipo.

    Ma sto divagando. Secondo il compilatore Java e gli implementatori di tipi generici, si dovrebbe sempre parametrizzare il vostro riferimento, anche se si sta utilizzando solo <?>.

  2. Non applicabile. Anche se si può sempre non dichiarare qualsiasi informazioni sul tipo, e quindi utilizzare @SuppressWarnings("unchecked") insieme ad un cast esplicito per fare il compilatore felice.

  3. La risposta di See Boemia.

  4. Non c'è davvero abbastanza informazioni da dire. Anche se la parte superiore della mia testa, io non sono sicuro di quello che il caso d'uso reale sarebbe quello di avere "una classe generica che rappresenta un insieme di oggetti generici". Perché non usare le raccolte direttamente?

Altri suggerimenti

C'è quasi sempre un modo per parametrizzare il codice. In breve, sarei scrivendo UserClass, in questo modo:

public UserClass <N extends Number> {
     MyCollection<N> c;

     public method(MyObject<N> o){
          c.foo(o);      
     }
} 

Modifica
Se siete preoccupati per la vostra poluting codice del client con i generici, si può fare una classe tipizzata astratta e fornire implementazioni non tipizzato, in questo modo:

public abstract UserClass <N extends Number> {
     MyCollection<N> c;

     public method(MyObject<N> o){
          c.foo(o);      
     }
} 

public class DoubleUserClass extends UserClass<Double> {}

Per ridurre classe troppo grosso, si può mettere le classi non tipizzato all'interno la classe di base:

public abstract UserClass <N extends Number> {
     MyCollection<N> c;

     public method(MyObject<N> o){
          c.foo(o);      
     }

     public static class DoubleImpl extends UserClass<Double> {}
     public static class FloatImpl extends UserClass<Float> {}
     public static class IntegerImpl extends UserClass<Integer> {}
} 

E li usa in questo modo: new UserClass.DoubleImpl() etc

E 'facile prendere la mano con i generici. Devi smettere da qualche parte. Quindi no, non credo che non la parametrizzazione di un riferimento a un tipo generico è sempre una cosa negativa. A volte lo sforzo supplementare necessario per ottenere tutti i farmaci generici esattamente a destra proprio non ne vale la pena.

Tuttavia, è meglio specificare farmaci generici, ove possibile, naturalmente. Nel tuo esempio, si potrebbe aggiungere un parametro generico per UserClass come suggerisce Boemia.

Per quanto riguarda se la vostra collezione è una cattiva progettazione: l'interfaccia / infrastrutture che si sta definendo qui sembra troppo generali. Che cosa ha MyCollection che non può essere fatto con semplice java.util.List<YourClass>? C'è davvero bisogno di un'interfaccia MyObject? Se si vuole 'tag' un certo tipo di classi, sicuramente si può trovare una descrizione più specifica di ciò che rende questi oggetti diverso da qualsiasi altro Object?

 MyCollection<?> b; 

 public method2(MyObject<?> o){
      b.foo(o); 
 }

Se c'è una garanzia di out-of-band che b.foo(o) è sicuro, quindi abbiamo bisogno di fare questo lavoro chiamata. Introdurre un metodo di supporto

<N extends Number>
void foo(MyCollection<N> collection, MyObject<?> obj)
{
    @SuppressWarnings("unchecked")
    MyObject<N> obj2 = (MyObject<N>)obj;

    collection.foo(obj2);
}

MyCollection<?> b;
void method2(MyObject<?> o){
     foo(b, o); // now works
}

Il cast da MyObject<?> a MyObject<N> è sicuro, a causa della gaurantee out-of-band. Si tratta di una controllata gettato in questo senso, così noi siamo giustificati per eliminare l'avviso.

In un'applicazione reale mondo non è raro che sappiamo di più su relazioni di tipo rispetto alla lingua può esprimere; nessuna necessità di essere programmatore di scusa se sa di più sul suo codice rispetto al compilatore.

Casting farmaci generici che coinvolgono è un po 'difficile; non è intuitivo come funziona foo(b,o).

E 'bene utilizzare i tipi di grezzo MyCollection, MyObject per evitare il fastidio. Ignorare l'avviso che non potrai usare il tipo grezzo. Che è una sciocchezza.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top