Necesitamos tu apoyo para compartir nuestros escritos referente a las ciencias informáticas.
Solución:
Esto ciertamente es posible con el enlace recursivo, pero los constructores de subtipos también deben ser genéricos y se necesitan algunas clases abstractas provisionales. Es un poco engorroso, pero sigue siendo más fácil que la versión no genérica.
/**
* Extend this for Mammal subtype builders.
*/
abstract class GenericMammalBuilder>
String sex;
String name;
B sex(String sex)
this.sex = sex;
return self();
B name(String name)
this.name = name;
return self();
abstract Mammal build();
@SuppressWarnings("unchecked")
final B self()
return (B) this;
/**
* Use this to actually build new Mammal instances.
*/
final class MammalBuilder extends GenericMammalBuilder
@Override
Mammal build()
return new Mammal(this);
/**
* Extend this for Rabbit subtype builders, e.g. LopBuilder.
*/
abstract class GenericRabbitBuilder>
extends GenericMammalBuilder
Color furColor;
B furColor(Color furColor)
this.furColor = furColor;
return self();
@Override
abstract Rabbit build();
/**
* Use this to actually build new Rabbit instances.
*/
final class RabbitBuilder extends GenericRabbitBuilder
@Override
Rabbit build()
return new Rabbit(this);
Hay una manera de evitar tener las clases hoja “concretas”, donde si tuviéramos esto:
class MammalBuilder>
...
class RabbitBuilder>
extends MammalBuilder
...
Luego, debe crear nuevas instancias con un diamante y usar comodines en el tipo de referencia:
static RabbitBuilder> builder()
return new RabbitBuilder<>();
Eso funciona porque el límite en la variable de tipo asegura que todos los métodos de, por ejemplo, RabbitBuilder
tener un tipo de retorno con RabbitBuilder
, incluso cuando el argumento de tipo es solo un comodín.
Sin embargo, no soy muy fan de eso, porque necesitas usar comodines en todas partes, y solo puedes crear una nueva instancia usando el diamante o un tipo crudo. Supongo que terminas con un poco de incomodidad de cualquier manera.
Y por cierto, sobre esto:
@SuppressWarnings("unchecked")
final B self()
return (B) this;
Hay una forma de evitar ese elenco sin marcar, que es hacer que el método sea abstracto:
abstract B self();
Y luego anótelo en la subclase de hoja:
@Override
RabbitBuilder self() return this;
El problema de hacerlo de esa manera es que, aunque es más seguro para los tipos, la subclase puede devolver algo diferente a this
. Básicamente, de cualquier manera, la subclase tiene la oportunidad de hacer algo mal, por lo que realmente no veo muchas razones para preferir uno de esos enfoques sobre el otro.
Si alguien todavía se encuentra con el mismo problema, sugiero la siguiente solución, que se ajusta al patrón de diseño “Preferir composición sobre herencia”.
Clase para padres
El elemento principal es la interfaz que el constructor de la clase padre debe implementar:
public interface RabbitBuilder
public T sex(String sex);
public T name(String name);
Aquí está la clase principal modificada con el cambio:
public class Rabbit
public String sex;
public String name;
public Rabbit(Builder builder)
sex = builder.sex;
name = builder.name;
public static class Builder implements RabbitBuilder
protected String sex;
protected String name;
public Builder()
public Rabbit build()
return new Rabbit(this);
@Override
public Builder sex(String sex)
this.sex = sex;
return this;
@Override
public Builder name(String name)
this.name = name;
return this;
La clase infantil
La clase infantil Builder
debe implementar la misma interfaz (con diferente tipo genérico):
public static class LopBuilder implements RabbitBuilder
Dentro de la clase infantil Builder
el campo que hace referencia al padreBuilder
:
private Rabbit.Builder baseBuilder;
esto asegura que el padre Builder
Los métodos se llaman en el niño, sin embargo, su implementación es diferente:
@Override
public LopBuilder sex(String sex)
baseBuilder.sex(sex);
return this;
@Override
public LopBuilder name(String name)
baseBuilder.name(name);
return this;
public Rabbit build()
return new Lop(this);
El constructor de Builder:
public LopBuilder()
baseBuilder = new Rabbit.Builder();
El constructor de la clase secundaria compilada:
public Lop(LopBuilder builder)
super(builder.baseBuilder);
Enfrentado con el mismo problema, utilicé la solución propuesta por emcmanus en: https://community.oracle.com/blogs/emcmanus/2010/10/24/using-builder-pattern-subclasses
Solo estoy volviendo a copiar su solución preferida aquí. Digamos que tenemos dos clases, Shape
y Rectangle
. Rectangle
hereda de Shape
.
public class Shape
private final double opacity;
public double getOpacity()
return opacity;
protected static abstract class Init>
private double opacity;
protected abstract T self();
public T opacity(double opacity)
this.opacity = opacity;
return self();
public Shape build()
return new Shape(this);
public static class Builder extends Init
@Override
protected Builder self()
return this;
protected Shape(Init> init)
this.opacity = init.opacity;
Ahí está el Init
clase interna, que es abstracta, y la Builder
clase interna, que es una implementación real. Será útil a la hora de implementar Rectangle
:
public class Rectangle extends Shape
private final double height;
public double getHeight()
return height;
protected static abstract class Init> extends Shape.Init
private double height;
public T height(double height)
this.height = height;
return self();
public Rectangle build()
return new Rectangle(this);
public static class Builder extends Init
@Override
protected Builder self()
return this;
protected Rectangle(Init> init)
super(init);
this.height = init.height;
Para instanciar el Rectangle
:
new Rectangle.Builder().opacity(1.0D).height(1.0D).build();
De nuevo, un resumen Init
clase, heredando de Shape.Init
y un Build
esa es la implementación real. Cada Builder
clase implementar el self
método, que es responsable de devolver una versión correctamente emitida de sí mismo.
Shape.Init <-- Shape.Builder
^
|
|
Rectangle.Init <-- Rectangle.Builder
Comentarios y puntuaciones del tutorial
Si te animas, tienes la opción de dejar un artículo acerca de qué le añadirías a este post.