2010-07-08 25 views
17

Jak mam zadzwonić pod numer start()?Generic Java + Builder pattern

package com.example.test; 

class Bar {} 

public class Foo<K> 
{ 
    final private int count; 
    final private K key; 

    Foo(Builder<K> b) 
    { 
     this.count = b.count; 
     this.key = b.key; 
    } 

    public static class Builder<K2> 
    { 
     int count; 
     K2 key; 

     private Builder() {} 
     static public <K3> Builder<K3> start() { return new Builder<K3>(); } 
     public Builder<K2> setCount(int count) { this.count = count; return this; } 
     public Builder<K2> setKey(K2 key) { this.key = key; return this; } 
     public Foo<K2> build() { return new Foo(this); } 
    } 

    public static void main(String[] args) 
    { 
     Bar bar = new Bar(); 
     Foo<Bar> foo1 = Foo.Builder.start().setCount(1).setKey(bar).build(); 
     // Type mismatch: cannot convert from Foo<Object> to Foo<Bar> 

     Foo<Bar> foo2 = Foo.Builder<Bar>.start().setCount(1).setKey(bar).build(); 
     // Multiple markers at this line 
     // - Bar cannot be resolved 
     // - Foo.Builder cannot be resolved 
     // - Syntax error on token ".", delete this token 
     // - The method start() is undefined for the type Foo<K> 
     // - Duplicate local variable fooType mismatch: cannot convert from Foo<Object> to Foo<Bar> 

     Foo<Bar> foo3 = Foo<Bar>.Builder.start().setCount(1).setKey(bar).build(); 
     // Multiple markers at this line 
     // - Foo cannot be resolved 
     // - Syntax error on token ".", delete this token 
     // - Bar cannot be resolved  
    } 
} 

Odpowiedz

23

Byłaś blisko:

Foo.Builder.<Bar> start().setCount(1).setKey(bar).build(); 

Cheers! :)

P.S. Jeśli kompilator nie może samodzielnie określić parametru typu metody, można go wymusić, wywołując obj.<Type> method(...).

P.P.S możesz użyć:

public Foo<K2> build() { 
    return new Foo<K2>(this); 
} 

Unikaj używania surowych typy.

+1

za każdym razem, gdy zapisujesz jawny typ argumentu w metodzie, bóg zabija kotka;) – sfussenegger

+0

aha, więc w ten sposób określasz statyczne metody z typami rodzajowymi. dzięki! –

+0

@sfussenegger: ok, więc czy jest inny sposób na zrobienie tego? –

13

Metoda Andrei jest w porządku, ale większość programistów prawdopodobnie będzie borykała się z raczej nieznaną składnią. Korzystanie z tej metody może być łatwiejsze:

static public <K3> Builder<K3> start(Class<K3> cls) { return new Builder<K3>(); } 

Foo<Bar> foo1 = Foo.Builder.start(Bar.class).setCount(1).setKey(bar).build(); 

Ta klasa jest przekazywana tylko w celu pomocy w typie ogólnym. Nie jest ładny, ale przynajmniej składnia jest powszechnie znana.

Innym rozwiązaniem byłoby zacząć od razu z obiektem typu rodzajowego:

Foo<Bar> foo1 = Foo.Builder.startWithKey(bar).setCount(1).build(); 
-2

To jest mój wdrożenie generycznego budowniczy w java 8.

public class Builder<T> { 
    private T instance; 
    private boolean ifCond = true; // default 
    public Builder(Class<T> clazz){ 
     try { 
      instance = clazz.newInstance(); 
     } catch (InstantiationException | IllegalAccessException e) { 
      e.printStackTrace(); 
     } 
    } 

    public Builder<T> with(Consumer<T> setter){ 
     if(ifCond) 
      setter.accept(instance); 
     return this; 
    } 

    public T get(){ 
     return instance; 
    } 

    public static <T> Builder<T> build(Class<T> clazz) { 
     return new Builder<>(clazz); 
    } 

    public Builder<T> If(BooleanSupplier condition){ 
     this.ifCond = condition.getAsBoolean(); 
     return this; 
    } 

    public Builder<T> endIf(){ 
     this.ifCond = true; 
     return this; 
    } 
} 

How to use it: 

List list = Arrays.asList(1, 2, 3, 4, 5) 

System.out.println(
    Builder.build(Sample.class) 
      .with(s -> s.setId(1)) 
      .with(s -> s.setName("Sample object")) 
      .with(s -> s.setList(list)) 
      .get() 
); 

// Java Properties 
System.out.println(
    Builder.build(Properties.class) 
      .with(p -> p.put("one", 1)) 
      .with(p -> p.put("two", 2)) 
      ... 
      .get() 

); 
System.out.println(Builder.build(Properties.class) 
        .with(p -> p.put("one", 1)) 
        .If(() -> false) // any expression return boolean 
        .with(p -> p.put("two", 2)) 
        .with(p -> p.put("three", 3)) 
        .endIf() 
        .with(p -> p.put("four", 4)) 
        .get() 

); // output=> {one=1, four=4} 

https://howtocodetutorial.wordpress.com/generic-builder-pattern-in-java-8/

+0

Nie dam ci głosowania w dół, tylko wyjaśnienie, dlaczego nie jest to dobry przykład ani praktyka. Celem stosowania wzorca budowniczego jest unikanie dużych konstruktorów, a dzieje się tak tylko w przypadku ** niezmiennych ** obiektów. Jeśli masz zmienny obiekt, możesz po prostu utworzyć instancję i wywołać settery. Ale z niezmienną, musisz przekazać wszystkie wartości w konstruktorze, to ma problem lub wykładniczy wzrost kombinacji konstruktorów, stąd wzór konstruktora. Budowniczy obiektów powinien więc być niezmienny. W twoim przykładzie używasz setterów i z tym stracisz cały cel konstrukcji budowniczych. – Vajda