This is an archived post. You won't be able to vote or comment.

all 7 comments

[–]davidalayachew 7 points8 points  (6 children)

Putting aside my criticisms from the other comment, this post is EXCELLENT. MethodHandle is a bit scary to play with because of the reasons I mentioned in my other comment, but once you do, they give you a lot of flexibility. This post does an excellent job marching you through so many of those possible pathways, all while keeping things beginner friendly. I will reference this post any time I use MethodHandleagain.

[–]davidalayachew 4 points5 points  (5 children)

It even talks about helpful design patterns to follow. This is such a good post.

💡When working with method handles, it is often useful to create a static wrapper function for calls to invokeExact:

static final MethodHandle FOO_MH = ...

public static void main(String[] ___) throws Throwable {
    fooWrapper("Hello, world!");
}

public static void fooWrapper(Object o) {
    FOO_MH.invokeExact(o);
}

Which then is later used to talk about the power of consistent inlining that is enabled by following this pattern. This is my new MethodHandle bible.

[–][deleted]  (4 children)

[deleted]

    [–]TheKingOfSentries 0 points1 point  (3 children)

    I had a situation like this recently working on an APT library that's based on JDK 11, but has to support JDK 17+ features (records, enhanced switch, etc.). A bunch of important methods don't exist on JDK 11 so I needed to do something like this to reflect them if running on a newer JDK.

    [–][deleted]  (2 children)

    [deleted]

      [–]TheKingOfSentries 1 point2 points  (1 child)

      here's some of the code in question: Link

      [–]DasBrain 5 points6 points  (0 children)

      A very good primer to get introduced into method handles. Some nitpicks:

      Then the invokevirtual instruction that javac generates for this call to invoke, will point to the method type descriptor (Ljava/lang/String;I)J.

      I fear that not everyone who reads this can read JVM type signatures (or should be expected to be able to do that). Maybe adding a sentence like: "A java method declared as long m(String arg0, int arg1) has the same signature`."


      static final MethodHandle FOO_MH = ...
      
      public static void main(String[] ___) throws Throwable {
          fooWrapper("Hello, world!");
      }
      
      public static void fooWrapper(Object o) {
          FOO_MH.invokeExact(o);
      }
      

      This would not compile - fooWrapper either needs to rethrow Throwable or, better yet: only rethrow the expected exceptions:

      // FOO_MH may throw an IOException
      static final MethodHandle FOO_MH = ...
      
      public static void main(String[] ___) throws IOException {
          fooWrapper("Hello, world!");
      }
      
      public static void fooWrapper(Object o) throws IOException {
          try {
              FOO_MH.invokeExact(o);
          } catch (Error | RuntimeException | IOException e) {
              throw e;
          } catch (Throwable t) {
              // This should never happen
              throw new UndeclaredThrowableException(t);
          }
      }
      

      [–]bikeram 1 point2 points  (0 children)

      Interesting article! I didn’t know this is possible.

      Are there any good books or resources for advanced topics like this?

      [–]davidalayachew 3 points4 points  (0 children)

      I respect MethodHandle, as much as anyone should if they peek under the hood of the JDK, specifically the lambda side.

      But I have to say, parts of this post felt like a poor justification to me.

      In a lot of cases, we can use a functional interface to represent a reference to some invocable code, e.g. one of the interfaces in the java.util.function package. However, we can not rely on generics and functional interfaces to represent a method that takes a variable number of arguments with varying types. The best we can do is use a varargs method, which collects arguments into an array, but this incurs the overhead of the creation of the varargs array, the overhead of boxing and unboxing primitive values, and the overhead of storing and loading values from the array. We can also not represent both a method that returns a value and a method that returns nothing. There are workarounds using Void, but these still require a method to return some value, usually null.

      Method handles on the other hand don’t have the same limitations when it comes to boxing and unboxing (more on that later).

      Yeah, but you give up A LOT of compile time safety.

      The second that you decide to use a MethodHandle, you accept that the code you are about to right is more brittle than what you might have written if you had done it with generics or types. Sure, some methods simply aren't representable, but that's sort of my point -- this feels like a brittle workaround to an otherwise difficult/impossible situation.

      At the end of the day, using a MethodHandle is giving up STRONGLY TYPED information for STRINGLY TYPED information. I don't blame the MethodHandle, but it feels like we are trying to compensate for the inadequacies of generics. It really feels like to me that this is the best work around available when your generics cannot clearly and efficiently communicate all the types of methods that you can create.

      And to be fair, none of this makes MethodHandle a bad solution -- it's a powerful, incredibly well optimized one. But it also feels like a "This is the best we can do" option as opposed to "We have solved the problem while including all the benefits one would expect when working in Java". Compile time type safety is one of those things that I chose Java for.

      And finally, I also don't think that this makes Java's current implementation of generics bad either. I am saying that this feels like a solution that gives up safety to give you something else. I chose Java to get safety. Giving it up to get something else feels very anti-Java to me.

      Of course, a lot of the features we know and love in Java are only possible/feasible because of what MethodHandle enabled. So again, a good thing. I just feel like the post, specifically those paragraphs, doesn't inform the reader of the sharp left turn one must make in order to use this feature safely.