2011-02-15 11 views
118

Recientemente comencé a leer Java efectivo por Joshua Bloch. Encontré la idea del patrón Builder [Artículo 2 en el libro] realmente interesante. Traté de implementarlo en mi proyecto, pero hubo errores de compilación. A continuación se presenta, en esencia, lo que estaba tratando de hacer:Patrón de generador en Java efectivo

La clase con múltiples atributos y su clase constructor:

public class NutritionalFacts { 
    private int sodium; 
    private int fat; 
    private int carbo; 

    public class Builder { 
     private int sodium; 
     private int fat; 
     private int carbo; 

     public Builder(int s) { 
      this.sodium = s; 
     } 

     public Builder fat(int f) { 
      this.fat = f; 
      return this; 
     } 

     public Builder carbo(int c) { 
      this.carbo = c; 
      return this; 
     } 

     public NutritionalFacts build() { 
      return new NutritionalFacts(this); 
     } 
    } 

    private NutritionalFacts(Builder b) { 
     this.sodium = b.sodium; 
     this.fat = b.fat; 
     this.carbo = b.carbo; 
    } 
} 

Clase donde trato de usar la clase anterior:

public class Main { 
    public static void main(String args[]) { 
     NutritionalFacts n = 
      new NutritionalFacts.Builder(10).carbo(23).fat(1).build(); 
    } 
} 

I recibo el siguiente error del compilador:

an enclosing instance that contains effectivejava.BuilderPattern.NutritionalFacts.Builder is required NutritionalFacts n = new NutritionalFacts.Builder(10).carbo(23).fat(1).build();

No entiendo lo que significa el mensaje. Por favor explique. El código anterior es similar al ejemplo sugerido por Bloch en su libro.

+1

posible duplicado de [Un ejemplo encerrando que contiene Se requiere] (http://stackoverflow.com/questions/ 4297857/an-enclosing-instance-that-contains-my-reference-is-required) –

Respuesta

145

Haga que el constructor sea una clase static. Entonces funcionará. Si no es estático, requerirá una instancia de su clase propietaria, y el punto es no tener una instancia de él, e incluso prohibir la creación de instancias sin el constructor.

public class NutritionFacts { 
    public static class Builder { 
    } 
} 

Referencia: Nested classes

+25

Y, de hecho, 'Builder' es' estático' en el ejemplo del libro (página 14, línea 10 en la 2ª edición). – Powerlord

4

Esto significa que no puede crear el tipo de adjuntar. Esto significa que primero debe cerar una instancia de clase "principal" y luego desde esta instancia puede crear instancias de clase anidadas.

NutritionalFacts n = new NutritionalFacts() 

Builder b = new n.Builder(10).carbo(23).fat(1).build(); 

Nested Classes

+3

que no tiene mucho sentido, porque necesita que el constructor construya los "hechos", y no al revés. – Bozho

+4

cierto si nos centramos en el patrón del generador, me centré solo en "No entiendo lo que significa el mensaje" y presenté una de las dos soluciones. –

7

tiene que declarar la clase interna Builder como static.

Consulte alguna documentación para non-static inner classes y static inner classes.

Básicamente, las instancias de clases internas no estáticas no pueden existir sin una instancia de clase externa adjunta.

4

La clase de generador debe ser estática. No tengo tiempo ahora mismo para probar el código más allá de eso, pero si no funciona, avíseme y volveré a analizarlo.

11

Está intentando acceder a una clase no estática de una manera estática. Cambie Builder a static class Builder y debería funcionar.

El uso de ejemplos que realiza falla porque no hay ninguna instancia de Builder presente. Una clase estática para todos los propósitos prácticos siempre se instancia. Si no lo hace estática, que había necesidad de decir:

Widget = new Widget.Builder(10).setparm1(1).setparm2(3).build(); 

Debido a que se necesita para construir un nuevo Builder cada vez.

22

Usted debe hacer la clase Builder como estática y también debe hacer los campos final y tener getters para conseguir esos valores. No proporcione setters a esos valores. De esta manera, tu clase será perfectamente inmutable.

public class NutritionalFacts { 
    private final int sodium; 
    private final int fat; 
    private final int carbo; 

    public int getSodium(){ 
     return sodium; 
    } 

    public int getfat(){ 
     return fat; 
    } 

    public int getCarbo(){ 
     return carbo; 
    } 

    public static class Builder { 
     private int sodium; 
     private int fat; 
     private int carbo; 

     public Builder sodium(int s) { 
      this.sodium = s; 
      return this; 
     } 

     public Builder fat(int f) { 
      this.fat = f; 
      return this; 
     } 

     public Builder carbo(int c) { 
      this.carbo = c; 
      return this; 
     } 

     public NutritionalFacts build() { 
      return new NutritionalFacts(this); 
     } 
    } 

    private NutritionalFacts(Builder b) { 
     this.sodium = b.sodium; 
     this.fat = b.fat; 
     this.carbo = b.carbo; 
    } 
} 

Y ahora se pueden establecer las propiedades de la siguiente manera:

NutritionalFacts n = new NutritionalFacts.Builder().sodium(10).carbo(15). 
fat(5).build(); 
+0

¿Por qué no hacer que los campos sean públicos? Ya son definitivos, y aún así serían inmutables. –