Java -Generika und Designmuster: Es ist immer eine schlechte Sache, eine Verweise auf einen generischen Typ zu parametrisieren?

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

Frage

Diese Frage hängt teilweise mit meinem letzten zusammen Frage.

Ich habe eine generische Klasse, die eine Sammlung generischer Objekte darstellt:

public interface MyObject<N extends Number>{}

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

In meinem Design werden diese Sammlungen von Objekten von einer Client -Klasse (nennen wir es CollectionBuilder) über einen abstrakten Klassenansatz erstellt:

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

}

Generische Sammlungen sollten auf diese Weise konstruiert werden:

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

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

Ok. CollectionBuilder ist sich des generischen Betontyps bewusst (doppelt in diesem Fall doppelt) und kann korrekt erstellen. Ich kann dies ohne Warnung zusammenstellen und alle sollten gut funktionieren.

Jetzt habe ich eine Frage sowohl mit Generika als auch mit dem Design meines Programms.

Ich habe eine andere Klasse in meiner Anwendung, die die von CollectionBuilder erstellte Sammlung verwenden muss (nennen wir diese Klasse UserClass). Die Benutzerklasse:

  1. Muss nicht wissen, welcher bestimmte Betontyp seine Sammlung ist (MyCollection<MyObject<Double>> oder MyCollection<MyObject<Integer>>).
  2. Führen Sie einige Operationen in diesen Sammlungen durch, in denen einige in der MyCollection -Schnittstelle definierte Methoden angegeben sind.
  3. Ich möchte ihm keinen generischen Typ hinzufügen.

Ist in der beschriebenen Situation eine schlechte Idee nicht die generische Klassenmycollection -behinderte Benutzerklasse parametrisieren?

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
     }
}

Der Java -Compiler protestiert immer mit einer Warnung, wenn ich den generischen Parameter nicht ansehe. Wie auch immer, von der Innenbenutzerklasse kenne ich den konkreten Parameter (und ich würde es gerne nicht wissen) und deklariere ihn sogar mit "?" Erlauben Sie mir nicht, die Methode Foo bei MyCollection anzurufen.

Die Frage sind also:

  1. Eine Parametrisierung eines Verweiss auf einen generischen Typ ist immer eine schlechte Sache?
  2. Wenn die Antwort bei 1 Nein ist, ist dies eine richtige Situation, um es nicht zu tun?
  3. Wenn die Antwort bei 1 Ja lautet, wie kann ich die MyCollection -Methode von Innennutzerklasse verwenden, ohne ihre generischen Typen zu kennen?
  4. Ist meins ein schlechtes Design?

Ich hoffe, klar gewesen zu sein. Ich habe von Tagen mit diesem Problem zu kämpfen und habe keine Ahnung. Bitte hilf mir.

War es hilfreich?

Lösung

  1. Nach Angaben des Java -Compilers ja. Und Diese Antwort Enthält viele gute Punkte, warum. Persönlich bin ich in einigen Situationen nicht einverstanden, insbesondere wo der Gebrauch von Class ist besorgt (wie zum Beispiel, wenn Sie a haben Map<Class, Object>). In solchen Fällen müssen immer angehalten werden <?> Bis zum Ende der "Klasse" fühlt sich einfach wie unnötig an, und macht den Code nicht lesbarer.

    Die Idee, haben Class Parametrisiert sein, basierend auf der Art von Objekt, mit der es zugeordnet ist, hat sich für mich in erster Linie immer etwas zweifelhaft gefühlt. Der Punkt von Class ist eine gemeinsame Schnittstelle zu haben, mit der Informationen über jedes Objekt (ALA -Reflexion) unabhängig von ihrem Typ erhalten werden können.

    Aber ich schweife ab. Gemäß dem Java -Compiler und den Implementierern generischer Typen sollten Sie Ihre Referenz immer parametrisieren, auch wenn Sie nur verwenden <?>.

  2. Unzutreffend. Sie können jedoch immer keine Informationen angeben und dann verwenden @SuppressWarnings("unchecked") zusammen mit einer expliziten Besetzung, um den Compiler glücklich zu machen.

  3. Siehe Bohemians Antwort.

  4. Es gibt nicht wirklich genügend Informationen zu sagen. Ich bin mir jedoch nicht sicher, was der tatsächliche Anwendungsfall für "eine generische Klasse, die eine Sammlung generischer Objekte darstellt". Warum nicht einfach die Sammlungen direkt verwenden?

Andere Tipps

Es gibt fast immer eine Möglichkeit, Ihren Code zu parameterisieren. Kurz gesagt würde ich die Benutzerklasse wie folgt eingeben:

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

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

Bearbeiten
Wenn Sie sich Sorgen machen, Ihren Client-Code mit Generika zu polutieren, können Sie eine abstrakt getippte Klasse erstellen und nicht-Typ-Implementierungen wie folgt bereitstellen:

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

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

public class DoubleUserClass extends UserClass<Double> {}

Um das Aufblähen der Klassen zu reduzieren, können Sie die Nicht-Typ-Klassen abgeben Innerhalb die Basisklasse:

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> {}
} 

Und benutze sie so: new UserClass.DoubleImpl() etc

Es ist einfach, mit Generika mitgerissen zu werden. Du musst irgendwo anhalten. Also nein, ich denke nicht, dass es immer eine schlechte Sache ist, einen Verweis auf einen generischen Typ zu parameterisieren. Manchmal ist es nicht wert, die zusätzlichen Anstrengungen zu erreichen, die alle Generika genau richtig machen.

Natürlich ist es jedoch besser, Generika nach Möglichkeit zu spezifizieren. In Ihrem Beispiel können Sie der Benutzerklasse einen generischen Parameter hinzufügen, wie Bohemian vorschlägt.

Ob Ihre Sammlung ein schlechtes Design ist: Die Schnittstellen/Infrastruktur, die Sie hier definieren, scheint übermäßig allgemein zu sein. Was macht MyCollection Habe das nicht einfach mit einfach mit java.util.List<YourClass>? Gibt es wirklich ein Bedürfnis nach einem MyObject Schnittstelle? Wenn Sie eine bestimmte Art von Klassen "taggen" möchten, können Sie sicherlich eine spezifischere Beschreibung dessen finden, was diese Objekte von jedem anderen unterscheiden Object?

 MyCollection<?> b; 

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

Wenn es eine außergewöhnliche Garantie gibt, garantiert das b.foo(o) ist sicher, dann müssen wir diesen Anruf funktionieren. Stellen Sie eine Helfermethode ein

<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
}

Die Besetzung von MyObject<?> zu MyObject<N> ist sicher, aufgrund des außergewöhnlichen Gaurantene. In diesem Sinne ist es eine geprüfte Besetzung, daher sind wir gerechtfertigt, die Warnung zu unterdrücken.

In einer realen Anwendung ist es nicht selten, dass wir mehr über Typbeziehungen wissen, als die Sprache ausdrücken kann. Kein Programmierer muss sich entschuldigen, wenn er mehr über seinen Code weiß als den Compiler.

Casting mit Generika ist etwas schwierig; Es ist nicht intuitiv wie foo(b,o) Arbeiten.

Es ist in Ordnung, Rohtypen zu verwenden MyCollection, MyObject Um den Ärger zu vermeiden. Ignorieren Sie die Warnung, dass Sie keinen rohen Typ verwenden dürfen. Das ist Unsinn.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top