Create Java8 softlink programmatically

Just a theoretical question, I currently have no practical use.

Assuming that some of my API accepts a link to a function as an argument, and I would like to either pass it directly from the code through the syntax "::", or collect the corresponding functions through reflection, store on some map and call it conditionally.

Can I programmatically convert methodto Consumer<String>?

Map<String, Consumer<String>> consumers = new HashMap<>();
consumers.put("println", System.out::println);

Method method = PrintStream.class.getMethod("println", String.class);
consumers.put("println", makeFunctionReference(method));
...
myapi.feedInto(consumers.get(someInput.getConsumerId()));

Update

Although not satisfied with the solutions in the current answers, but after getting a hint about LambdaMetaFactory, I tried to compile this code

public class TestImpl {
    public static void FnForString(String arg) {}
}

public class Test {
    void test() {
        List<String> strings = new ArrayList<>();
        Consumer<String> stringConsumer = TestImpl::FnForString;

        strings.stream().forEach(stringConsumer);
        strings.stream().forEach(TestImpl::FnForString);
        stringConsumer.accept("test");
    }
}

and after submitting only the test class to CFR decompiler I go back:

public class Test {
    void test() {
        ArrayList strings = new ArrayList();
        Consumer<String> stringConsumer = 
            (Consumer<String>)LambdaMetafactory.metafactory(
                null, null, null, 
                (Ljava/lang/Object;)V, 
                FnForString(java.lang.String), 
                (Ljava/lang/String;)V)();
        strings.stream().forEach(stringConsumer);
        strings.stream().forEach(
            (Consumer<String>)LambdaMetafactory.metafactory(
                null, null, null, 
                (Ljava/lang/Object;)V, 
                FnForString(java.lang.String ), 
                (Ljava/lang/String;)V)());
        stringConsumer.accept("test");
    }
}

From this, I see that:

  • It is somehow possible to do in the style of "1-liner"
  • , (Ljava/lang/Object;)V ( ) . MethodType metafactory(). - "/" -, , , .
  • (offtop) - - , .

... Test TestImpl, CFR , .

+4
3

:

consumers.put("println", s -> {
    try {
        method.invoke(System.out, s);
    } catch (InvocationTargetException | IllegalAccessException e) {
        throw new RuntimeException(e);
    }
});

, , (.. invokedynamic), MethodHandle. , .

MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodType methodType = MethodType.methodType(void.class, String.class);
MethodHandle handle = lookup.findVirtual(PrintStream.class, "println", methodType);

consumers.put("println", s -> {
    try {
        handle.invokeExact(System.out, s);
    } catch (Throwable e) {
        throw new RuntimeException(e);
    }
});

consumers.get("println").accept("foo");

MethodHandles.Lookup. MethodHandle. MethodType, , : , void (, void.class) (, String.class). , println PrintStream.

( , MethodHandle .

+5

, - .

final Method m = ...
final T target = ...

Consumer<String> c = (arg1) => m.invoke(t, arg1);

LambdaMetaFactory , , , Map, , , .


- "1-"

, -, . .

, -. , .

, (Ljava/lang/Object;) V ( ) . MethodType (). , "/" -, , , .

invokedynamic. , JVM , java, . java.lang.invoke, , .

Java, , CalaSite dynamicInvoker MH static final MethodHandle invokeExact.

(offtop) - - , .

, , , , .

+3

, , Java 8, .

- invokedynamic , Java. :

public static void main(String... arg) {
    Consumer<String> consumer=getConsumer();
    consumer.accept("hello world");
}

static Consumer<String> getConsumer() {
    try {
        MethodHandles.Lookup lookup=MethodHandles.lookup();
        MethodType consumeString = MethodType.methodType(void.class, String.class);
        return (Consumer<String>)LambdaMetafactory.metafactory(lookup, "accept",
            MethodType.methodType(Consumer.class, PrintStream.class),
            consumeString.changeParameterType(0, Object.class),
            lookup.findVirtual(PrintStream.class, "println", consumeString), consumeString)
        .getTarget().invokeExact(System.out);
    }
    catch(RuntimeException | Error e) { throw e; }
    catch(Throwable t) { throw new BootstrapMethodError(t); }
}

, , getConsumer(), invokedynamic, MethodHandle MethodType, , . , Java.

, Consumer<String>, getConsumer(), System.out::println ( Consumer<String>) .

You can study Brian Goetz's Translation of Lambda Expressions for a deeper understanding of how this works. In addition, the APILambdaMetafactory documentation is pretty comprehensive.

0
source

Source: https://habr.com/ru/post/1616787/


All Articles