2010-07-08 3 views
17

Comment puis-je appeler start() ci-dessous?Génériques Java + Motif 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  
    } 
} 

Répondre

23

Vous étiez près:

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

Cheers! :)

P.S. Si le compilateur ne peut pas déduire le paramètre type de la méthode, vous pouvez le forcer en appelant obj.<Type> method(...).

P.P.S vous pouvez utiliser:

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

Évitez d'utiliser les types bruts.

+1

chaque fois que vous écrivez un argument de type explicite dans une méthode, dieu tue un chaton;) – sfussenegger

+0

aha, c'est ainsi que vous spécifiez des méthodes statiques avec des types génériques. Merci! –

+0

@sfussenegger: ok, y a-t-il un autre moyen de faire ça? –

13

La méthode d'Andrei est correcte, mais la plupart des programmeurs auront probablement des difficultés avec la syntaxe plutôt inconnue. Il peut être plus facile à utiliser de cette façon:

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 n'est transmise que pour aider avec le type générique. Ce n'est pas joli, mais au moins la syntaxe est de notoriété publique.

Une autre option serait de commencer tout de suite avec un objet du type générique:

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

Ceci est ma mise en œuvre du modèle de constructeur générique en 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

Je ne vais pas vous donner un vote négatif, juste une explication pourquoi ce n'est pas un bon exemple ni pratique. L'objectif global d'avoir un modèle de constructeur est d'éviter les grands constructeurs, et cela ne se produit qu'avec des objets ** immuables **. Si vous avez un objet mutable, vous pouvez juste créer une instance et appeler les setters. Mais avec immuable, vous devez passer toutes les valeurs dans le constructeur, cela pose un problème ou une croissance exponentielle des combinaisons constructeurs, donc un modèle constructeur. Ainsi, les objets créés par le constructeur devraient être immuables. Dans votre exemple, vous utilisez des setters et avec ce but du motif de constructeur est perdu. – Vajda