我正在尝试重构以下代码:
class Base {
private Object a, b, <...>; // there's like 10 of these attributes of different type
public Object a() {
return a;
}
public Object b() {
return b;
}
// more getters like the ones above
}
class RootNode extends Base { }
class BranchNode extends Base {
private RootNode root; // passed via constructor
public Object a() {
Object value = super.a();
return value != null ? value : root.a();
}
public Object b() {
Object value = super.b();
return value != null ? value : root.b();
}
// below are more methods like the above one, all with same logic
}
当然,我想删除此代码中的重复,以避免在添加新属性时输入更多相同的行,但我无法弄清楚如何做到这一点。
我的第一直觉是它看起来很像这个代码(不幸的是,它不能编译):
private <T> T nvlGet(Function<Base, T> accessor) {
T value = accessor.apply(super); // this is the problem line, because there is no way to pass a "super-reference" to anything
return value != null ? value : accessor.apply(root);
}
// and then public accessors would look like this:
public Object a() {
return nvlGet(Base::a);
}
我无法通过调用accessor.apply(this)
而不是accessor.apply(super)
来“修复”上述代码,因为这会导致Stack Overflow
错误。
最近我设法提出使用绑定供应商,如下:
private <T> T nvlGet(Supplier<T> first, Supplier<T> second) {
T value = first.get();
return value != null ? value : second.get();
}
public Object a() {
return nvlGet(super::a, root::a);
}
然而,对于相同的方法,这是我想要在理想世界中的两倍。所以,我想知道我是否遗漏了某些东西,我仍然能以某种方式修复使用Function<Base, T>
的版本
答案 0 :(得分:4)
没有“超级引用”这样的东西会改变普通调用可覆盖方法(aka invokevirtual
指令)的结果。
只要您坚持使用函数,使用两个方法引用的解决方案是您可以获得的最佳解决方案,而传递评估值则更简单:
private <T> T nvlGet(T value, Supplier<T> second) {
return value != null? value: second.get();
}
public Object a() {
return nvlGet(super.a(), root::a);
}
答案 1 :(得分:3)
正如其他人所说,你可以做得更好,因为super
不是你可以传递的参考。
我同意this answer,因为传递super.a()
,super.b()
等调用返回的值更简单。
此外,我会将第二个参数更改为Function<? super Base, ? extends T>
类型,以便root
方法中nvlGet
实例的使用仍然封装:
private <T> T nvlGet(T nullable, Function<? super Base, ? extends T> second) {
return nullable != null ? nullable : second.apply(root);
}
用法:
public Object a() {
return nvlGet(super.a(), Base::a);
}
public Object b() {
return nvlGet(super.b(), Base::b);
}
答案 2 :(得分:0)
可以使用允许动态处理的动态代理类;使用反射实现接口。然而,它并不像纯粹的反射那么昂贵。
但首先考虑一个替代方案:使用Optional<A> a()
,因为它肯定不那么人为,不那么巴洛克。
使用界面并创建InvocationHandler:
interface Abc {
public A a();
public B b();
}
public Class AbcProxy implements InvocationHandler {
private final Object obj;
public AbcProxy(Object obj) {
this.obj = obj;
}
public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
try {
...
return ...;
} catch (InvocationTargetException e) {
throw e.getTargetException();
} catch (Exception e) {
throw e;
}
}
}
用法可能不是很直观,只有a()
,b()
,我无法制作合理的示例代码。所以最好从示例代码开始。