2017-07-27 2 views
0

Ich möchte eine parametrisierte (generische) Klasse MyType<T> und seine Erbauer erstellen. Der Builder verfügt über einige Methoden, die den Typ T und einige Methoden, die den Typ verwenden, ignorieren. aber es scheint, wie mit diesem Muster Ich habe die T Erklärung wiederholen:Java-Typ Inferenz in Builder-Muster

     how to omit this declaration? 
            | 
            V 
MyType<String> myType = Builder.<String>of() 
           .method1(argument_can_be_of_any_type) 
           .method2(argument_must_be_of_type_T = String) 
           .build(); 

ist es möglich, einige Java-Syntax Tricks oder andere Design-Muster zu verwenden, um die zweite Typdeklaration zu verzichten und die API benutzerfreundlicher machen? wie zum Beispiel:

List<String> aList = Lists.emptyList(); 

oder

List<String> aList = new LinkedList<>(); 

ich will keine Reihenfolge von Methoden im Builder erzwingen

+0

Bitte poste 'Builder' Klasse. – saka1029

+0

Muss das Argument für method2 vom Typ String sein? (Mit anderen Worten, können wir den eventuellen Typ des Rückgabewerts von build() aus diesem Argument ableiten oder kann es einfach irgendetwas sein? – DeathB4Decaf

+1

Relevant: https://stackoverflow.com/questions/9058430/why-doesnt-immutablemap -builder-build-pick-the-correct-type-Parameter – shmosel

Antwort

0

Ich habe Dinge überarbeitet ein wenig, da meine ursprüngliche Antwort didn‘ t arbeiten wie angekündigt. (Dank shmosel das Problem für das Auffinden und Daniel Pryden für die vorgeschlagene Lösung.)

/* contents of Box.java */ 
public class Box<T> 
{ 
    private T contents; 
    private Object data; 

    protected Box(T contents, Object data) { 
     this.contents = contents; 
     this.data = data; 
    } 

    public static BoxBuilder builder() { 
     return new BoxBuilder(); 
    } 

    public T getContents() { 
     return contents; 
    } 
} 

/* contents of BoxBuilder.java */ 
public class BoxBuilder 
{ 
    private Object data; 

    public BoxBuilder withAnything(Object o) { 
     this.data = o; 
     return this; 
    } 

    // Infers new type from argument 
    public <T> TypedBoxBuilder<T> withBoxContent(T contents) { 
     TypedBoxBuilder<T> builder = new TypedBoxBuilder<T>(); 
     builder.setData(data); 
     builder.setContents(contents); 

     return builder; 
    } 
} 

/* contents of TypedBoxBuilder.java */ 
public class TypedBoxBuilder<T> 
{ 
    private T contents; 
    private Object data; 

    public TypedBoxBuilder() { 
    } 

    public TypedBoxBuilder<T> withAnything(Object data) { 
     this.data = data; 
     return this; 
    } 

    public TypedBoxBuilder<T> withContents(T contents) { 
     this.contents = contents; 
     return this; 
    } 

    public Box<T> build() { 
     return new Box<T>(contents, data); 
    } 

    public void setContents(T contents) { 
     this.contents = contents; 
    } 

    public void setData(Object data) { 
     this.data = data; 
    } 
} 

Und hier ist der Client-Code:

Box<String> box = Box.builder() // Returns BoxBuilder 
     .withBoxContent("FOO") // Returns TypedBoxBuilder<String> 
     .withAnything(42) // Returns TypedBoxBuilder<String> 
     .build(); // Returns Box<String> 
String c = box.getContents(); 

Und das funktioniert auch:

Box<String> box = Box.builder() // Returns BoxBuilder 
     .withAnything(42) // Returns BoxBuilder 
     .withBoxContent("FOO") // Returns TypedBoxBuilder<String> 
     .build(); // Returns Box<String> 
String c = box.getContents(); 
+2

Dies ist eine schreckliche Idee.Der generische Parameter ist nutzlos, wenn Sie einen rohen Typ verwenden, nicht zu erwähnen, dass es alle nicht verwandten Generika im selben löschen wird Wenn Sie diese 'Methode2 (argument_must_be_of_type_T)' 'vermissen? – shmosel

+2

Das ist so nah. Was Sie brauchen, ist zwei Builder-Typen,' Builder' und 'TypedBuilder erweitert Builder' der() '' kann 'Builder' zurückgeben (nicht generisch) und' withBoxContent (T) 'kann' TypedBuilder 'zurückgeben. –

+0

@smoselos, schöner Fang. Ich sah das Problem nicht, die anderen Generika zu löschen. Der Code wurde kompiliert und korrekt ausgeführt, aber als ich Ihren Kommentar gesehen habe, habe ich nachgesehen und Sie haben Recht - es ist nicht typsicher. Und Daniel, ich sehe, wohin du gehst, denke ich, und ich habe ein bisschen damit herumgebastelt, aber es hat immer noch das Problem, dass Shmosel entdeckt wurde. Ich * möchte * eine nette Möglichkeit sehen, so etwas zu tun (sicher den Typ der gebauten Instanz von Argumenten zum Builder abzuleiten), aber ich sehe keinen Weg, um es zum Laufen zu bringen. – DeathB4Decaf