2010-07-08 23 views
17

¿Cómo llamo a start() a continuación?Java genéricos + Generador patrón

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

Respuesta

23

estaban cerca:

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

Salud! :)

P.S. Si el compilador no puede inferir el parámetro de tipo del método en sí mismo, puede forzarlo llamando al obj.<Type> method(...).

P.P.S es posible que desee utilizar:

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

Evitar el uso de tipos de primas.

+1

cada vez que escriba un argumento de tipo explícito en un método, dios mata a un gatito;) – sfussenegger

+0

aha, así es como se especifican los métodos estáticos con los tipos genéricos. ¡Gracias! –

+0

@sfussenegger: bien, ¿hay otra forma de hacer esto? –

13

El método de Andrei está bien, pero la mayoría de los programadores probablemente tendrán dificultades con la sintaxis bastante desconocida. Puede ser más fácil de usar de esta forma:

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 clase solo se pasa para ayudar con el tipo genérico. No es bonito, pero al menos la sintaxis es de conocimiento común.

Otra opción sería comenzar de inmediato con un objeto de tipo genérico:

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

Ésta es mi ejecución del Builder genérica 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

No le daré un voto negativo, solo una explicación de por qué esto no es un buen ejemplo ni una práctica. Todo el propósito de tener un patrón de generador es evitar grandes constructores, y eso solo ocurre con ** objetos ** inmutables. Si tienes un objeto mutable, puedes crear una instancia y llamar a los setters. Pero con inmutable, debe pasar todos los valores en el constructor, esto representa un problema o un crecimiento exponencial de las combinaciones de constructores, por lo tanto, el patrón del constructor. Entonces, las creaciones de objetos creadas deben ser inmutables. En su ejemplo, usted está utilizando setters y con ese propósito completo de patrón de generador se pierde. – Vajda