javalambdaspring-kafkamethod-reference

How to specify a method reference sequence?


I've been trying to hack a bit around this very ugly looking piece of code in my codebase. The original code looks like this:

Long variable = Optional.of(message)
    .map(Message::getPayload)
    .map(MessageContentClass::getVariable)
    .orElse(null);

Since it's repeated loads of times in the codebase, this was really annoying me. I've already managed to simplify this into a function like this:

public <T, K> T getVariableFromMessage(Message<K> message, Function<K, T> func) {
   return Optional.of(message)
      .map(Message::getPayload)
      .map(func)
      .orElse(null);
}

and it works nicely. However, there's also some message types, where the desired variable is nested a bit deeper into the message. Something along the lines of:

Long variable = Optional.of(message)
    .map(Message::getPayload)
    .map(MessageContentClass::getObject1)
    .map(Object1::getObject2)
    .map(Object2::getVariable)
    .orElse(null);

I don't even know if it's possible, but it would be neat to generify the first functions, so that I have (instead of a single Function<K,T>) an array of functions, where each one "points" to the type of the next, so that the first map would go from K -> T, the second one from T -> R and so on, until it would get to the final one. I've seen method declarations, where a generic type is introduced OUTSIDE the angle brackets, so perhaps something like that? It's also clear that what I'm looking for would work for any number of functions. If I wanted just two or three, I could simply put them individually as arguments.

Hope it's clear what I'm trying to do and thanks for reading the whole post :)


Solution

  • You can't achieve this directly, at least in a type-safe way.

    But your current method, accepting a function, supports chaining the mapping operations indirectly with Function.andThen().

    Returns a composed function that first applies this function to its input, and then applies the after function to the result. If evaluation of either function throws an exception, it is relayed to the caller of the composed function.

    Function<MessageContentClass> first = MessageContentClass::getObject1;
    Function<MessageContentClass, Variable> finalFunc = first
      .andThen(Object1::getObject2)
      .andThen(Object2::getVariable);
    Variable variable = getVariableFromMessage(message, finalFunc);