2010-07-08 22 views
17

Come si chiama start() di seguito?Generici Java + Pattern builder

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

risposta

23

Si erano vicini:

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

Cheers! :)

P.S. Se il compilatore non può dedurre da solo il parametro type del metodo, è possibile forzarlo chiamando obj.<Type> method(...).

P.P.S si potrebbe desiderare di utilizzare:

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

Evitare l'uso di tipi prime.

+1

ogni volta che scrivi un argomento di tipo esplicito in un metodo, dio uccide un gattino;) – sfussenegger

+0

aha, ecco come si specificano i metodi statici con tipi generici. Grazie! –

+0

@sfussenegger: ok, quindi c'è un altro modo per farlo? –

13

Il metodo di Andrei è ok, ma la maggior parte dei programmatori probabilmente avrà problemi con la sintassi piuttosto sconosciuta. Potrebbe essere più semplice da utilizzare in questo modo:

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(); 

La classe viene passata solo per aiutare con il tipo generico. Non è bello, ma almeno la sintassi è conoscenza comune.

Un'altra opzione sarebbe quella di iniziare subito con un oggetto di tipo generico:

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

Questa è la mia realizzazione del builder generica in 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

Non ti darò un voto negativo, solo una spiegazione del perché questo non è un buon esempio né una pratica. L'intero scopo di avere un modello di builder è di evitare grandi costruttori, e questo accade solo con ** oggetti immutabili **. Se hai un oggetto mutabile, puoi semplicemente creare un'istanza e chiamare i setter. Ma con immutabile, è necessario passare tutti i valori nel costruttore, questo ha un problema o una crescita esponenziale delle combinazioni di costruttori, quindi un modello di builder. Quindi, crea oggetti creati deve essere immutabile. Nel tuo esempio, stai usando setter e con questo scopo il modello di builder è perso. – Vajda