Create non-capturing method reference which will call superclass method

M. Prokhorov :

I'm trying to refactor the following code:

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
}

Naturally, I want to remove the repetition in this code to save myself from typing even more of the same lines when new properties are added, but I cannot quite figure out how to do that.

My first instinct was that it looks a lot like this code (which, unfortunately, does not compile):

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

I can't obviously "fix" the above code by calling accessor.apply(this) instead of accessor.apply(super), because that would cause a Stack Overflow error.

Closest I managed to come up with so far is using bound suppliers, like this:

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

However, that is two times as many references to same method than I'd like to have in ideal world. So, I would like to know if I'm missing something, and I can still somehow fix the version which uses Function<Base, T>

Holger :

There is no such thing as a “super reference” that would alter the outcome of an ordinary invocation of an overridable method (aka invokevirtual instruction).

Your solution using two method references is the best you can get, as long as you insist on using functions, whereas passing evaluated values is even simpler:

private <T> T nvlGet(T value, Supplier<T> second) {
  return value != null? value: second.get();
}
public Object a() {
  return nvlGet(super.a(), root::a);
}

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=443223&siteId=1