创建将调用超类方法的非捕获方法引用

Create non-capturing method reference which will call superclass method

我正在尝试重构以下代码:

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) 来 "fix" 上面的代码,因为那会导致 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>

的版本

正如其他人所说,你不能做得更好,因为 super 不是你可以传递的参考。

我同意 的观点,即传递 super.a()super.b() 等调用返回的值更简单。

此外,我会将第二个参数更改为 Function<? super Base, ? extends T> 类型,以便 nvlGet 方法中 root 实例的用法保持封装:

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);
}

可以使用允许动态处理的动态代理class;使用反射实现接口。然而,它并不像纯反射那样昂贵。

但首先考虑一个替代方案:使用 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(),我无法做出合理的示例代码。所以也许最好从示例代码开始。

没有“超级引用”这样的东西会改变普通调用可覆盖方法(又名 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);
}