1

I was looking for a solution like this:
(in more detail: for some maven plugin running different build code in several phases/goals):

  void foo() {

    execCode( () -> doBar() ) ;
    execCode( () -> new File("/baz.txt").create() ) ;
    execCode( () -> { 
      System.out.println( "test" ) ; 
      throw new MojoFailureException("problem 1") ; 
    }) ;
  }


  void execCode( Code code ) throws MojoFailureException {

    try {

      // ... some common initialization / param parsing here ...

      code.run() ;

    } catch( Exception e ) {

      // ... common exception handling throwing e.g. MojoFailureException ...
    }
  }

supporting (primarily (E), (G) and (S) below):

  • (E): the code blocks throwing any java.lang.Exception
  • (V): no in or out values/objects needed
  • (S): short, easy to read/write code (minimal clutter)
    • also avoiding IDE warnings and dealing with it (e.g. via @Ignore for unused generic type parameters)
  • (8): Java 8+ support
  • (L): no further lib/jar dependencies, if possible (very common ones like Google Guava or Apache Commons would be fine as well)
  • (G): generic context (e.g. some build tasks) where it is fine to just know, that something should/will be done, but the proper exception handling and other stuff (common to each call) should be encapsulated
Andreas Covidiot
  • 4,286
  • 5
  • 51
  • 96

1 Answers1

1

best found strategy in short:

import java.util.concurrent.Callable;

// ...

  void foo() {

    execCode( () -> doBar() ) ;
    execCode( () -> 5 + 2 ) ;
    execCode( () -> { System.out.println( "test" ) ; return 0 ; }) ;
  }


  void execCode( Callable<Object> code ) {

    try {

      code.call() ;

    } catch( Exception e ) {

      // ...
    }
  }

more details:

solutions partially found in other answers and their disadvantages for above:

  • (S.L): using java.util.concurrent.Callable (above solution)
    • fulfills all above requirements
    • would also nicely work with some return value, if needed
  • (S.F): using java.util.Function
    • does not support (E)
    • especially with (V) it's more clutter ((S)) and semantically confusing
    • e.g. void foo() { execCode( (i) -> doBar() ) ... void execCode( Function< Void, Void > code ) { ... code.apply( null ) ... }
    • see also this answer
  • (S.I): creating some custom interface (class) to accomplish it
    • contradicts (S)
    • e.g. interface Code { abstract void run() throws Exception; }
    • see also this answer
  • (S.M): using method references like this::doBar
    • special case which may be very short and elegant if the code is (already) in some objects method contributing to (S)
    • would work with execCode( Callable<Object> ... ) { ... }
    • but not with execCode( Runnable ... ) { ... }
      • contradicts (E)
    • see also this answer
  • (S.R): using java.lang.Runnable
    • contradicts (E) and thus (S) since it would only allow to throw RuntimeException but no checked ones
    • see also this answer
  • (S.A): using java.lang.AutoCloseable
    • it matches the (E), (V) and (L) requirements, but it needs at least some comments to explain, why there is a execCode( AutoCloseable code ) { ... code.close() ; ... } although the code does not have to do anything with a semantic closing context :)
  • (S.C): using java.util.function.Consumer
    • does not support (E)
    • see also this answer* (S.X): using java.lang.reflect.Method
    • would contradict especially (S)
    • see also this answer
  • (S.7): command pattern solution
    • if it must be Java 7- compatible (contradicting (8)) this answer with the command pattern may be some good solution
Andreas Covidiot
  • 4,286
  • 5
  • 51
  • 96
  • 1
    You can also write [`execCode(Executors.callable(() -> System.out.println( "test" )));`](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/Executors.html#callable(java.lang.Runnable)) – Holger Mar 22 '22 at 07:11
  • @Holger ok, but it's not an advantage/solution for the main requirements, especially because *(G)* and also *(E)* are not covered. (these `execCode( ...)` calls do not happen only in `foo() { ... }`!) – Andreas Covidiot Mar 23 '22 at 10:37
  • 1
    I’m just referring to your own example; instead of `execCode( () -> { System.out.println( "test" ) ; return 0 ; }) ;` you can write `execCode(Executors.callable(() -> System.out.println( "test" )));` That’s just an addendum, not a solution on its own. Of course, when checked exceptions are involved, you can and have to resort to `{ statement; return null; }`. By the way, there’s also a functional interface for `void` that allows to throw exceptions, `AutoCloseable`, but maybe, using it for general purpose actions might be confusing to the reader. – Holger Mar 23 '22 at 10:45
  • @Holger the `return 0` was just to indicate that there may be arbitrary big code blocks with multiple statements. regarding `AutoClosable` :) didn't you see the *(S.A)* solution that exactly mentions this? – Andreas Covidiot Mar 23 '22 at 10:48
  • 2
    No, I didn’t see it. It’s easy to overlook something in this list, which unnecessarily discusses non-solutions in a verbose way. E.g., why discuss `java.util.Function`, `java.lang.Runnable`, and `java.util.function.Consumer` lengthy when they are out of the game due to not supporting exceptions? Why is “using method references” even treated as a solution? There is no difference between `() -> doBar()` and `this::doBar`, whether it works, depends on the functional interface. – Holger Mar 23 '22 at 10:59
  • @Holger: if you don't want to understand something/somebody, I can't help it. They are all solutions to a very common and general problem as suggested in the list and varying solutions. Searching the web and finding such condensed info like here is much more valuable than focusing on tiny differences of the same kind of question. The best answers for me and likely others are the ones, that do not only fix my possibly wrong code lines, but fix my thinking of the problem itself and seeing it in the broader scope. But I agree that this thinking is not applied by the majority. – Andreas Covidiot Mar 24 '22 at 11:32
  • @Holger: there is a difference between `() -> doBar()` and `this::doBar`. if you do not know it doesn't mean it isn't there :) try it out like I described above and you won't be able to compile the latter. you answer things you do not know for sure :-( – Andreas Covidiot Mar 24 '22 at 11:37
  • “if you do not know it doesn't mean it isn't there” Why don’t you enlighten us and *tell* the difference (relevant to your answer) that you claim to exist? Your “example” is just incomplete code, even the syntax given in your answer (`this::doBar()`) is invalid. It’s easy to show that `() -> doBar()` and `this::doBar` work equally well, https://ideone.com/ftWzfl If that doesn’t match you use case, it’s a problem of your incomplete “example.” – Holger Mar 24 '22 at 11:53
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/243280/discussion-between-andreas-covidiot-and-holger). – Andreas Covidiot Mar 24 '22 at 12:13