如何继承Builder类扩展选项且无调用顺序限制?
解决Builder继承后的链式调用顺序问题
这个问题我之前也碰到过,确实挺头疼的——继承Builder类后,父类方法返回父类类型,直接打断了子类的链式调用,还莫名其妙限制了调用顺序。不过有个经典的解决方案,既能保持你想要的访问限制(父类不能调用子类的方法),又能支持无顺序的链式调用,就是用泛型递归类型(CRTP,奇异递归模板模式)。
核心思路:让父类方法返回子类类型
我们可以给父类Builder添加泛型参数,这个参数指定为子类本身。父类的所有链式方法都返回这个泛型类型,这样子类继承时,就能让父类方法返回子类实例,从而继续调用子类特有的方法。
修改后的代码示例
import java.awt.geom.Point2D; public class PointBuilderDemo { // 父类添加泛型参数T,限制为PointBuilder的子类 private static class PointBuilder<T extends PointBuilder<T>> { private double x = 0.0; protected double y = 0.0; @SuppressWarnings("unchecked") T withX(double x) { this.x = x; // 这里的this实际是子类实例,强制转换为T是安全的 return (T) this; } Point2D build() { return new Point2D.Double(x, y); } } // 子类继承时,指定泛型参数为自身 private static class PointBuilderWithY extends PointBuilder<PointBuilderWithY> { PointBuilderWithY withY(double y) { this.y = y; return this; } } public static void main(String[] args) { Point2D pt1 = new PointBuilder<>() .withX(5.0) // .withY(3.0) // 依然无法编译,符合预期 .build(); // 先Y后X,正常工作 Point2D pt2 = new PointBuilderWithY() .withY(3.0) .withX(5.0) .build(); // 先X后Y,现在也能正常编译了! Point2D pt3 = new PointBuilderWithY() .withX(5.0) .withY(3.0) .build(); System.out.println(pt1); System.out.println(pt2); System.out.println(pt3); } }
为什么这个方案有效?
- 父类
PointBuilder的withX方法返回泛型类型T,而子类PointBuilderWithY继承时指定T为自身,所以调用withX后返回的是PointBuilderWithY实例,自然能继续调用withY方法。 - 父类依然没有
withY方法,所以PointBuilder实例无法调用该方法,完全符合你“仅让需要的子类使用withY”的设计初衷。 - 虽然有个
@SuppressWarnings("unchecked")的注解,但这里的类型转换是安全的——因为当子类实例调用withX时,this本身就是子类对象,强制转换为子类类型不会有运行时问题。
备选方案:重写父类方法(适合简单场景)
如果你的Builder方法不多,也可以在子类中重写父类的链式方法,让它返回子类类型:
private static class PointBuilderWithY extends PointBuilder { // 重写withX方法,返回PointBuilderWithY @Override PointBuilderWithY withX(double x) { super.withX(x); return this; } PointBuilderWithY withY(double y) { this.y = y; return this; } }
不过这个方案的缺点很明显:如果父类有很多链式方法,你需要逐个重写,代码冗余度很高,所以更推荐泛型递归的方案。
内容的提问来源于stack exchange,提问作者MiguelMunoz
相关产品推荐
相关产品推荐

