-3

There are many Java core functional interfaces.

I often need a slight variation with different arguments, e.g. for a custom listener with a custom event type, and none of the core classes fit.

Is there a generic way of writing a functional interface that covers more use cases?

Possible use cases including producers, consumers, listeners, handlers, strategies, delegates etc.

Triggered by the latest Java Magazine quiz about functional interfaces. Previous attempt deleted before I was able to edit it.

Reto Höhener
  • 5,419
  • 4
  • 39
  • 79
  • @user15793316 actually no. I think your suggestion highlights the difficulty of finding the correct functional interface. – Reto Höhener Jun 07 '21 at 08:32
  • @user15793316 you are right, sorry. My usage example has one argument and a return type. BiConsumer is void with 2 arguments. – Reto Höhener Jun 07 '21 at 08:37
  • @user15793316 yes `Function` would work for the example. Unfortunately none of the core versions throw Exception in the method signature, and I don't see any for 2 args and a return type. And I find the naming very inconsistent. – Reto Höhener Jun 07 '21 at 08:44
  • In my experience, exceptions work very well with lamdas. – Reto Höhener Jun 07 '21 at 08:57
  • Yes, I don't even know if it is possible to use custom interfaces with streams. Personally I miss Exception in the `Function` signature of `Stream.map`. It often forces me to try/catch, and my one-liner becomes multiline. – Reto Höhener Jun 07 '21 at 09:17

2 Answers2

3

Sure you can write your own Functional Interface(s), despite as many as possible will be nearly infinite (bound by size limitations of source and code files!)

The Functional Interfaces are specified in JLS 9.8 - basically an interface with a single abstract method.

But I would try to stick as much as possible to the standard interfaces from the java.util.function package (also a good source for examples of how to implement additional ones.)

  • I wanted to make the point that in practice, my variations with up to 3 arguments and void or return type cover almost every use case I had in the past 10 years. I also only have to remember a single name instead of 43. – Reto Höhener Jun 07 '21 at 08:52
  • Yes, I never had a need for those. I'm fine with boxing usually. I do agree with everything you said so far (except that exceptions do not work well with lamdas). I just thought someone else would find my approach useful. – Reto Höhener Jun 07 '21 at 09:02
1

A few years ago I created this set of generic functional interfaces with zero to three arguments, with and without return value. It covered 99% of my use cases since Java 8.

/**
 * Functional Interfaces
 */
public class Functions {
  /** 0 args, with return value */
  @FunctionalInterface
  public static interface Func0R<RETURN> {
    public RETURN run() throws Exception;

    default public RETURN runNoEx() {
      try {
        return run();
      }
      catch(Exception e) {
        throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e);
      }
    }
  }

  /** 1 args, with return value */
  @FunctionalInterface
  public static interface Func1R<ARG1, RETURN> {
    public RETURN run(ARG1 arg1) throws Exception;

    default public RETURN runNoEx(ARG1 arg1) {
      try {
        return run(arg1);
      }
      catch(Exception e) {
        throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e);
      }
    }
  }

  /** 2 args, with return value */
  @FunctionalInterface
  public static interface Func2R<ARG1, ARG2, RETURN> {
    public RETURN run(ARG1 arg1, ARG2 arg2) throws Exception;

    default public RETURN runNoEx(ARG1 arg1, ARG2 arg2) {
      try {
        return run(arg1, arg2);
      }
      catch(Exception e) {
        throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e);
      }
    }
  }

  /** 3 args, with return value */
  @FunctionalInterface
  public static interface Func3R<ARG1, ARG2, ARG3, RETURN> {
    public RETURN run(ARG1 arg1, ARG2 arg2, ARG3 arg3) throws Exception;

    default public RETURN runNoEx(ARG1 arg1, ARG2 arg2, ARG3 arg3) {
      try {
        return run(arg1, arg2, arg3);
      }
      catch(Exception e) {
        throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e);
      }
    }
  }

  /** 0 args, no return value (void) */
  @FunctionalInterface
  public interface Func0V {
    public void run() throws Exception;

    default public void runNoEx() {
      try {
        run();
      }
      catch(Exception e) {
        throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e);
      }
    }
  }

  /** 1 args, no return value (void) */
  @FunctionalInterface
  public static interface Func1V<ARG1> {
    public void run(ARG1 arg1) throws Exception;

    default public void runNoEx(ARG1 arg1) {
      try {
        run(arg1);
      }
      catch(Exception e) {
        throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e);
      }
    }
  }

  /** 2 args, no return value (void) */
  @FunctionalInterface
  public static interface Func2V<ARG1, ARG2> {
    public void run(ARG1 arg1, ARG2 arg2) throws Exception;

    default public void runNoEx(ARG1 arg1, ARG2 arg2) {
      try {
        run(arg1, arg2);
      }
      catch(Exception e) {
        throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e);
      }
    }
  }

  /** 3 args, no return value (void) */
  @FunctionalInterface
  public static interface Func3V<ARG1, ARG2, ARG3> {
    public void run(ARG1 arg1, ARG2 arg2, ARG3 arg3) throws Exception;

    default public void runNoEx(ARG1 arg1, ARG2 arg2, ARG3 arg3) {
      try {
        run(arg1, arg2, arg3);
      }
      catch(Exception e) {
        throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e);
      }
    }
  }
}

Sample usage:

public class Component {
  private Func1R<Component, Integer> strategy;

  public void setStrategy(Func1R<Component, Integer> strategy) {
    this.strategy = strategy;
  }

  public void calc() {
    int result = this.strategy.runNoEx(this);
    // ...
  }
}
Reto Höhener
  • 5,419
  • 4
  • 39
  • 79