代码之家  ›  专栏  ›  技术社区  ›  M. Prokhorov

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

  •  8
  • M. Prokhorov  · 技术社区  · 7 年前

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

    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 回复  |  直到 7 年前
        1
  •  4
  •   Holger    7 年前

    没有所谓的“超级引用”会改变可重写方法(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);
    }
    
        2
  •  3
  •   fps    7 年前

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

    我同意 this answer 通过传递 super.a() , super.b() ,etc调用更简单。

    此外,我将第二个参数更改为 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);
    }
    
        3
  •  0
  •   Joop Eggen    7 年前

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

    但首先考虑另一种选择:使用 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() ,我无法生成合理的示例代码。所以最好从示例代码开始。