2009-03-25 13 views
29

Ich habe eine ziemlich komplizierte Reihe von generischen Klassen in Java. Zum Beispiel habe ich eine SchnittstelleTyp-Aliase für Java-Generics

interface Doable<X,Y> { 
    X doIt(Y y); 
} 

und die Umsetzung

class DoableImpl implements Doable<Foo<Bar<Baz,Qux>>,Foo<Bar<Zot,Qux>>> { 
    Foo<Bar<Baz,Qux>> doIt(Foo<Bar<Zot,Qux>> fooBZQ) { ... } 
} 

In der realen Implementierung Doable ziemlich viele Methoden hat und so Foo<Bar<Baz,Qux>> usw., erscheinen immer und immer wieder. (Glauben Sie es oder nicht, die generischen Typen sind ein bisschen schmerzhafter als das. Ich habe sie für das Beispiel vereinfacht.)

Ich möchte diese vereinfachen, um mich selbst zu tippen und die Belastung zu erleichtern in meinen Augen. Was ich möchte, ist eine einfache "Typ Alias" für Foo<Bar<Baz,Qux>>, etc., sagen FooBBQ und FooBZQ.

Meine aktuelle Idee ist Wrapper-Klassen zu definieren:

class FooBBQ { 
    public static FooBBQ valueOf(Foo<Bar<Baz,Qux>> fooBBQ) { 
    return new FooBBQ(fooBBQ); 
    } 
    private Foo<Bar<Baz,Qux>> fooBBQ; 
    private FooBBQ(Foo<Bar<Baz,Qux>> fooBBQ) { 
    this.fooBBQ = fooBBQ; 
    } 
    public Foo<Bar<Baz,Qux>> toGeneric() { 
    return fooBBQ; 
    } 
} 

class FooBZQ { /* pretty much the same... */ } 

class DoableImpl implements Doable<FooBBQ,FooBZQ> { 
    FooBBQ doIt(FooBZQ fooBZQ) { ... } 
} 

Das funktioniert gut, aber es hat einige Nachteile:

  1. Wir müssen für jede generische Instanz separate Wrapper definieren. Die Wrapper-Klassen sind kurz und stilisiert, aber ich kann keinen Weg finden, sie zu macroisieren.
  2. Wir haben die Übersetzung Overhead (konzeptionell, wenn nicht operativ) von valueOf und toGeneric Aufruf zwischen FooBBQ und Foo<Bar<Baz,Qux>> zu konvertieren. wenn doIt Anrufe in eine Bibliotheksroutine zum Beispiel, die eine Foo<Bar<Zot,Qux>> erwartet (was die reale Umsetzung der Fall ist), haben wir am Ende mit etwas wie

    return FooBBQ.valueOf(libraryCall(fooBZQ.toGeneric())) 
    

    , wo würden wir ursprünglich hatten

    return libraryCall(fooBZQ); 
    

Gibt es eine andere Möglichkeit, das Verhalten "Typ-Alias" zu erhalten, das ich hier haben möchte? Vielleicht mit einem Makro-Toolset von Drittanbietern? Oder muss ich akzeptieren, dass ich viel tippen muss, in einer Richtung (mit den generischen Typen in der Implementierung) oder in der anderen (Wrapper für sie schreiben)? Vielleicht ist es eine schlechte Idee, so viele generische Parameter herumfliegen zu lassen, und ich muss das Problem überdenken.

[UPDATE] OK, ich verbiete weitere "Do not do that" Antworten. Nimm es als gegeben, dass Foo<Bar<Baz,Qux>> echten Wert in meiner Problemdomäne hat (Pete Kirkham kann Recht haben, dass es genug Wert hat, um eine richtige Wrapper-Klasse mit einem beschreibenden Namen zu erhalten). Aber das ist ein Programmierproblem; Versuchen Sie nicht, das Problem zu definieren.

+0

Ich werde 'wahrscheinlich ja' zum letzten sagen, habe aber kein konstruktives Feedback: P Es sieht aus wie ein Durcheinander! Viel Glück! –

+6

Ich denke, diese Frage wäre ein guter Vorschlag für Java. Wenn Sie Oracle eine Type Aliasing-Funktion vorschlagen könnten, wäre es meiner Meinung nach eine großartige Sprachfunktion, die die Sprache vereinfacht und den Quellcode lesbarer macht. –

+1

Knee tief in Java heute und wollte etwas verbose Code bereinigen ... nicht nur bekomme ich keine Typ-Inferenz, aber auch keinen Typ-Aliasing. * schluchz * –

Antwort

2

Vielleicht ist es eine schlechte Idee, so viele generische Parameter herumfliegen zu lassen, und ich muss das Problem überdenken.

Sehr wahrscheinlich. Müssen Sie "Doit" in 8 Dimensionen spezialisieren?

In vielen Fällen existieren diese Typen nicht in einem Vakuum, und Sie sollten sich überlegen, welche Domänenobjekte Ihr 'Wrapper' darstellt, anstatt sie als Kodierungskomfort zu verwenden.

0

Ich würde sagen, ja, Sie müssen das Problem überdenken. Die Deklaration

ist ein ziemlich klarer Fall von Übergebrauch Generika.

+0

Ich stimme nicht zu. Es kann nicht sein, zu viel Generika zu verwenden ... es kann nur eine unklare Verwendung von Generika sein. Wenn es zusätzliche Typsicherheit bietet, dann ist es vielleicht die Mühe wert, aber es muss auch einen Weg geben, dies deutlicher zu machen. – Eddie

+0

Würdest du mir glauben, wenn ich sage, dass es im echten Code eigentlich ziemlich klar (nur extrem ausführlich) ist? Offensichtlich hat es keinen Sinn, deine Objekte mit Foo, Bar und Qux zu parametrisieren ... –

+0

Ich habe (und zwar) Schlimmeres gesehen, es gibt Zeiten, in denen es Sinn macht ... aber sie können schwer zu folgen sein. Die Typsicherheit kann sich jedoch lohnen. – TofuBeer

9

Wenn Sie volle Art Sicherheit wollen, ich denke nicht, dass Sie besser ohne irgendeine Art von Wrapperklassen tun können. Aber machen, warum nicht diese Klassen erben/die ursprünglichen generischen Versionen implementieren, wie folgt aus:

public class FooBBQ extends Foo<Bar<Baz,Qux>> { 
... 
} 

Dadurch entfällt die Notwendigkeit für toGeneric() Verfahren, und es ist klar, meiner Meinung nach, dass es nur ein Typ alias ist . Ein generischer Typ kann auch ohne Compilerwarnung in FooBBQ umgewandelt werden. Es wäre meine persönliche Präferenz, Foo, Bar, Baz... Schnittstellen, wenn möglich, auch wenn einige Code-Duplizierung in der Implementierung auftreten würde.

Nun, ohne konkreten Problembereich zu kennen, ist es schwer zu sagen, ob Sie brauchen, sagen FooBBQ, wie in Ihrem Beispiel, oder vielleicht ein:

public class FooBar<X, Y> extends Foo<Bar<X, Y>> { 
... 
} 

Auf der anderen Seite, haben Sie einfach gedacht Konfigurieren des Java-Compilers, um einige der allgemeinen Warnungen nicht anzuzeigen, und einfach die Teile der generischen Definition weglassen? Oder verwenden Sie strategisch platzierte @SuppressWarnings("unchecked")? Mit anderen Worten, können Sie DoableImpl nur machen „teilweise genericized“:

class DoableImpl implements Doable<Foo<Bar>>,Foo<Bar>> { 
    Foo<Bar> doIt(Foo<Bar> foobar) { ... } 
} 

und die Warnungen aus Gründen der weniger Code Unordnung ignorieren? Noch einmal, ohne ein konkretes Beispiel schwer zu entscheiden, aber es ist noch eine andere Sache, die Sie versuchen können.

+0

Extending Foo > Sie erhalten eine unidirektionale Übersetzung (d. H. Implizites Up-Casting) auf Kosten von komplizierteren Konstruktoren (zum expliziten Down-Casting). Es ist mir nicht klar, dass das ein Gewinn ist. Ich möchte @SuppressWarnings Annotationen so gut wie möglich vermeiden. –

5

Scala hat eine gute Unterstützung für Typ-Aliase. Zum Beispiel:

type FooBBQ = Foo[Bar[Baz,Qux]] 

Mir ist klar, dass diese Antwort nicht hilfreich sein, wenn Sie nicht die Möglichkeit haben, zu Scala von Schalt. Aber wenn Sie die Möglichkeit haben, zu wechseln, könnte es Ihnen leichter fallen.

1

Nun, Java hat keine Typ-Aliase, so dass Sie kein Glück haben. Typ-Aliase können jedoch manchmal durch Typvariablen ersetzt werden! So lösen wir das Problem von zu vielen Generika mit noch mehr Generika!

Wie Sie alle Inhalte von Ihrem Beispiel gestrippt habe kann ich nicht erraten, wo oder ob es sinnvoll ist, zusätzliche Variablen vom Typ einzuführen, aber hier ist eine mögliche Zersetzung:

class DoableImplFoo<A,B> implements Doable<Foo<A>,Foo<B>> { 
    public DoableImplFoo(SomePropertyOf<A,B> aAndBAreGoodEnough) { ... } 
    Foo<A> doIt(Foo<B> fooB) { ... } 
} 

Wenn Sie A instanziiert zu Bar<Baz,Qux> und B bis Bar<Zot,Qux> später werden Sie vielleicht feststellen, dass es wieder ein bestimmtes Muster gibt, aber es könnte weniger sein als das, was Sie ursprünglich hatten.

Verwandte Themen