正如马克·西曼所说,这个问题可以用隐形眼镜来解决.
其 idea 是,Lens<A, B>
表示从A
的实例获取B
的方法,以及为A
的实例设置新的B
的方法.然后,您的班级的每个领域都可以用一个透镜来表示.
你可以合成镜片.如果你有Lens<A, B>
分和Lens<B, C>
分,你可以把它们合成到Lens<A, C>
分.这是设置深度嵌套字段的关键.你只需要将许多镜头合成在一起,然后在最终合成的镜头上调用set
.
ClassA modifiedClassA = ClassA.CLASS_B_LENS
.then(ClassB.CLASS_D_LENS) // I've called the composition operation "then"
.then(ClassD.FIELD_D_LENS)
.set(someClassA, "New Value!");
以下是一个可能的实现.
interface Lens<Root, T> {
T get(Root r);
// here I generalised the set operation to a "modify",
// where the previous value is also available
Root modify(Root root, UnaryOperator<T> newValue);
default Root set(Root root, T newValue) {
return modify(root, x -> newValue);
}
default <U> Lens<Root, U> then(Lens<T, U> p2) {
return new Lens<>() {
@Override
public U get(Root r) {
return p2.get(Lens.this.get(r));
}
@Override
public Root modify(Root r, UnaryOperator<U> f) {
return Lens.this.modify(r, t -> p2.modify(Lens.this.get(r), f));
}
};
}
}
然后,您可以制作这样的工厂方法,用Getters 和"枯萎剂"来制造透镜.后者可以由Lombok @With
批注生成.你可以把所有的空头支票都放在这家工厂里.
static <Root, T> Lens<Root, T> make(Function<Root, T> getter, BiFunction<Root, T, Root> setter) {
return new Lens<>() {
@Override
public T get(Root r) {
if (r == null) return null;
return getter.apply(r);
}
@Override
public Root modify(Root root, UnaryOperator<T> f) {
if (root == null) return null;
return setter.apply(root, f.apply(get(root)));
}
};
}
然后,您可以将一些镜头作为静态字段添加到类中.这一部分可以由代码生成器/注释处理器完成,如果您知道如何编写代码生成器/注释处理器的话.
@Value
@With
class ClassA {
private String fieldA;
private ClassB classB;
public static final Lens<ClassA, String> FIELD_A_LENS = Lens.make(ClassA::getFieldA, ClassA::withFieldA);
public static final Lens<ClassA, ClassB> CLASS_B_LENS = Lens.make(ClassA::getClassB, ClassA::withClassB);
}
@Value
@With
class ClassB {
private ClassC classC;
private ClassD classD;
public static final Lens<ClassB, ClassC> CLASS_C_LENS = Lens.make(ClassB::getClassC, ClassB::withClassC);
public static final Lens<ClassB, ClassD> CLASS_D_LENS = Lens.make(ClassB::getClassD, ClassB::withClassD);
}
// and so on...
另请参见this medium article,它采用的方法略有不同.