如何修复使用Function<Base,T>的代码,实现Getter逻辑无重复复用?
解决方法:通过父类辅助方法修复Function方案
好问题!你遇到的核心痛点是super是Java的关键字而非可引用的对象实例,所以没法直接传给Function.apply();而用this又会触发子类重写的getter,导致递归栈溢出。不过我们可以通过一个简单的父类辅助方法,让你最初的Function方案完美运行。
最优解决方案:给Base类添加辅助方法
我们可以在父类Base中新增一个方法,专门用来调用当前实例的父类版本getter,这样就能绕过子类的重写逻辑:
class Base { private Object a, b, <...>; // 原有属性 public Object a() { return a; } public Object b() { return b; } // 其他原有getter... // 新增辅助方法:调用当前实例的父类getter逻辑 protected <T> T getSuperValue(Function<Base, T> accessor) { return accessor.apply(this); } }
然后在BranchNode中,我们就可以实现你想要的nvlGet方法了:
class BranchNode extends Base { private RootNode root; public BranchNode(RootNode root) { this.root = root; } private <T> T nvlGet(Function<Base, T> accessor) { // 通过父类的辅助方法调用父类版本的getter T value = super.getSuperValue(accessor); return value != null ? value : accessor.apply(root); } // 现在getter只需要写一次方法引用 public Object a() { return nvlGet(Base::a); } public Object b() { return nvlGet(Base::b); } // 新增属性时,只需要添加一行这样的代码即可 }
原理说明
super.getSuperValue(accessor)会调用父类Base中的方法,此时方法内的this是Base类型的实例(虽然实际是BranchNode对象,但在父类方法中调用accessor.apply(this)时,会执行Base类中定义的getter,而非子类重写的版本),完美避开了递归栈溢出的问题。- 这个方案完全符合你的需求:新增属性时只需要添加一行
public Object x() { return nvlGet(Base::x); },不需要重复编写相同的空值判断逻辑。
可选方案:使用MethodHandles(不推荐)
如果你不想修改父类Base,可以通过Java的MethodHandles API直接调用父类的方法,但这个方案依赖反射,代码可读性和维护性较差:
class BranchNode extends Base { private RootNode root; public BranchNode(RootNode root) { this.root = root; } private <T> T nvlGet(Function<Base, T> accessor) { try { // 从Function中提取方法名(仅适用于方法引用的情况) Method method = accessor.getClass().getDeclaredMethods()[0]; MethodHandle mh = MethodHandles.lookup() .findSpecial(Base.class, method.getName(), MethodType.methodType(method.getReturnType()), BranchNode.class); // 调用父类版本的方法 T value = (T) mh.invoke(this); return value != null ? value : accessor.apply(root); } catch (Throwable e) { throw new RuntimeException("Failed to invoke super method", e); } } public Object a() { return nvlGet(Base::a); } public Object b() { return nvlGet(Base::b); } }
这个方案不需要修改父类,但代码更复杂,且对Function的实现有依赖(仅支持方法引用,不支持lambda表达式),所以优先推荐第一种方案。
对比你当前的Supplier方案
你现在用的Supplier方案虽然可行,但每次都要写两个方法引用(super::a和root::a),而修复后的Function方案只需要写一次Base::a,在新增属性时更简洁,也更不容易出错。
内容的提问来源于stack exchange,提问作者M. Prokhorov
相关产品推荐
相关产品推荐

