Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can we get a method name using java.util.function?

I tried to do:

public class HelloWorld {
     public static void main(String... args){
        final String string = "a";
        final Supplier<?> supplier = string::isEmpty;
        System.out.println(supplier);
     }
}

I get:

HelloWorld$$Lambda$1/471910020@548c4f57

I would like to get the string isEmpty. How can I do this?

EDIT: the code of the method I created is this one:

public class EnumHelper {
    private final static String values = "values";
    private final static String errorTpl = "Can't find element with value `{0}` for enum {1} using getter {2}()";

    public static <T extends Enum<T>, U> T getFromValue(T enumT, U value, String getter) {
        @SuppressWarnings("unchecked")
        final T[] elements = (T[]) ReflectionHelper.callMethod(enumT, values);

        for (final T enm: elements) {
            if (ReflectionHelper.callMethod(enm, getter).equals(value)) {
                return enm;
            }
        }

        throw new InvalidParameterException(MessageFormat.format(errorTpl, value, enumT, getter));
    }
}

The problem is I can't pass as parameter T::getValue, since getValue is not static. And I can't pass someEnumElem::getValue, since the get() will return the value of that element. I could use inside the for loop:

Supplier<U> getterSupllier = enm:getValue;
if (getterSupllier.get().equals(value)) {
    [...]
}

but in this way getValue is fixed, I can't pass it as parameter. I could use some third-party library to do an eval(), but I really don't want to open that Pandora vase :D

EDIT 2: Function does work with no parameters methods, but only in Java 11. Unluckily I'm stuck with Java 8.

like image 610
Marco Sulla Avatar asked Sep 11 '19 10:09

Marco Sulla


People also ask

How do you find the method name?

Method Class | getName() Method in Java Method class is helpful to get the name of methods, as a String. To get name of all methods of a class, get all the methods of that class object. Then call getName() on those method objects. Return Value: It returns the name of the method, as String.

How can I find the method that called the current method Java?

getMethodName() method returns the name of the method containing the execution point represented by this stack trace element. Thread. currentThread().

What is Java Util function?

util. function package which has been introduced since Java 8, to implement functional programming in Java. It represents a function which takes in one argument and produces a result.

How do you get the name of a method in Java?

Method Class | getName() Method in Java. The getName() method of java.lang.reflect.Method class is helpful to get the name of methods, as a String. To get name of all methods of a class, get all the methods of that class object. Then call getName() on those method objects. Syntax: Return Value: It returns the name of the method, as String.

How to get the name of currently executing method in Java?

Getting name of currently executing method is useful for handling exceptions and debugging purposes. Below are different methods to get currently executing method : Using Throwable Stack Trace : Using Throwable Class : In Java, Throwable class is the superclass of all exceptions and errors in java.lang package.

What are the methods of the function interface in Java?

It uses examples to show how the apply (), andThen (), compose () & identity () methods of the Function interface are to be used. Function<T, R> is an in-built functional interface introduced in Java 8 in the java.util.function package.

How to get all methods of a class in Java?

The getName () method of java.lang.reflect .Method class is helpful to get the name of methods, as a String. To get name of all methods of a class, get all the methods of that class object.


Video Answer


2 Answers

It’s weird that you are asking about the opposite of what you actually need.

You have a method that receives a string and wants to execute a method with that name, but for some unknown reason, you ask for the opposite, to get the method name from an existing supplier.

And already written in a comment before knowing the actual code, you can solve the actual problem by replacing the String getter parameter with Function<T,U> getter.

You don’t need any Reflection tool here:

public class EnumHelper {
    private final static String errorTpl
            = "Can't find element with value `{0}` for enum {1} using getter {2}()";

    public static <T extends Enum<T>, U> T getFromValue(
            T enumT, U value, Function<? super T, ?> getter) {

        final T[] elements = enumT.getDeclaringClass().getEnumConstants();

        for (final T enm: elements) {
            if(getter.apply(enm).equals(value)) {
                return enm;
            }
        }

        throw new IllegalArgumentException(
            MessageFormat.format(errorTpl, value, enumT, getter));
    }
}

The getter Function can be implemented via method reference, e.g.

ChronoUnit f = EnumHelper.getFromValue(
    ChronoUnit.FOREVER, Duration.ofMinutes(60), ChronoUnit::getDuration);
System.out.println(f);

I made the signature of the function parameter more generous compared to Function<T,U>, to raise the flexibility regarding existing functions, e.g.

Function<Object,Object> func = Object::toString;
ChronoUnit f1 = EnumHelper.getFromValue(ChronoUnit.FOREVER, "Years", func);
System.out.println(f1.name());

If printing meaningful names in the erroneous case is really important, just add a name parameter just for reporting:

public static <T extends Enum<T>, U> T getFromValue(
        T enumT, U value, Function<? super T, ?> getter, String getterName) {

    final T[] elements = enumT.getDeclaringClass().getEnumConstants();
    for (final T enm: elements) {
        if(getter.apply(enm).equals(value)) {
            return enm;
        }
    }

    throw new IllegalArgumentException(
            MessageFormat.format(errorTpl, value, enumT, getterName));
}

to be called like

ChronoUnit f = EnumHelper.getFromValue(
    ChronoUnit.FOREVER, Duration.ofMinutes(60), ChronoUnit::getDuration, "getDuration");

That’s still better than using Reflection for the normal operations…

like image 93
Holger Avatar answered Oct 29 '22 01:10

Holger


string::isEmpty will be constructed by a method LambdaMetafactory.metafactory which has implMethod among its parameters.

final String methodName = implMethod.internalMemberName().getName();

would return a method name (here, "isEmpty") if we had access to the arguments passed to this factory method, and to implMethod in particular. The arguments generated by up-calls from the JVM that provides very specific information for the java.lang.invoke API.

For example, to initialise a DirectMethodHandle which string::isEmpty represents, the JVM will call the following method.

/**
 * The JVM is resolving a CONSTANT_MethodHandle CP entry.  And it wants our help.
 * It will make an up-call to this method.  (Do not change the name or signature.)
 * The type argument is a Class for field requests and a MethodType for non-fields.
 * <p>
 * Recent versions of the JVM may also pass a resolved MemberName for the type.
 * In that case, the name is ignored and may be null.
 */
static MethodHandle linkMethodHandleConstant(Class<?> callerClass, int refKind,
                                             Class<?> defc, String name, Object type) 

That name (exactly what you requested) will be put there by the JVM, and there is no means for us to access it. For now.

To read:

  • Explicit use of LambdaMetafactory
  • What are CONSTANT_MethodHandle, CONSTANT_MethodType, and CONSTANT_InvokeDynamic?
  • MethodHandle - What is it all about?
like image 26
Andrew Tobilko Avatar answered Oct 29 '22 02:10

Andrew Tobilko