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

时间:2018-01-29 12:19:35

标签: java java-8 method-reference

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

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>的版本

3 个答案:

答案 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(),我无法制作合理的示例代码。所以最好从示例代码开始。