Java – why can’t I Invokeexact() is here, even if the methodtype is OK?

For one of my projects, I have to make dynamic calls to constructors But since this is java 7 and uses the "classic" reflection API, I use Java lang.invoke.

Code:

@ParametersAreNonnullByDefault
public class PathMatcherProvider
{
    private static final MethodHandles.Lookup LOOKUP
        = MethodHandles.publicLookup();
    private static final MethodType CONSTRUCTOR_TYPE
        = MethodType.methodType(void.class,String.class);

    private final Map<String,Class<? extends PathMatcher>> classMap
        = new HashMap<>();
    private final Map<Class<? extends PathMatcher>,MethodHandle> handleMap
        = new HashMap<>();

    public PathMatcherProvider()
    {
        registerPathMatcher("glob",GlobPathMatcher.class);
        registerPathMatcher("regex",RegexPathMatcher.class);
    }

    public final PathMatcher getPathMatcher(final String name,final String arg)
    {
        Objects.requireNonNull(name);
        Objects.requireNonNull(arg);

        final Class<? extends PathMatcher> c = classMap.get(name);
        if (c == null)
            throw new UnsupportedOperationException();

        try {
            return c.cast(handleMap.get(c).invoke(arg));
        } catch (Throwable throwable) {
            throw new RuntimeException("Unhandled exception",throwable);
        }
    }

    protected final void registerPathMatcher(@Nonnull final String name,@Nonnull final Class<? extends PathMatcher> matcherClass)
    {
        Objects.requireNonNull(name);
        Objects.requireNonNull(matcherClass);
        try {
            classMap.put(name,matcherClass);
            handleMap.put(matcherClass,findConstructor(matcherClass));
        } catch (NoSuchMethodException | illegalaccessexception e) {
            throw new RuntimeException("cannot find constructor",e);
        }
    }

    private static <T extends PathMatcher> MethodHandle findConstructor(
        final Class<T> matcherClass)
        throws NoSuchMethodException,illegalaccessexception
    {
        Objects.requireNonNull(matcherClass);
        return LOOKUP.findConstructor(matcherClass,CONSTRUCTOR_TYPE);
    }

    public static void main(final String... args)
    {
        new PathMatcherProvider().getPathMatcher("regex","^a");
    }
}

OK, this job

My problem is this line:

return c.cast(handleMap.get(c).invoke(arg));

If I replace invoke with invokeexact, I get this stack trace:

Exception in thread "main" java.lang.RuntimeException: Unhandled exception
    at com.github.fge.filesystem.path.matchers.PathMatcherProvider.getPathMatcher(PathMatcherProvider.java:62)
    at com.github.fge.filesystem.path.matchers.PathMatcherProvider.main(PathMatcherProvider.java:89)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
Caused by: java.lang.invoke.WrongMethodTypeException: expected (String)RegexPathMatcher but found (String)Object
    at java.lang.invoke.Invokers.newWrongMethodTypeException(Invokers.java:350)
    at java.lang.invoke.Invokers.checkExactType(Invokers.java:361)
    at com.github.fge.filesystem.path.matchers.PathMatcherProvider.getPathMatcher(PathMatcherProvider.java:60)

I don't quite understand that both globpathmatcher and regexpathmatcher use a constructor with a string as a parameter. Therefore, the methodtypes of both are in the constructor_ Defined in type If not, I can't "catch" methodhandles

But I got a wrong methodtypeexception Why?

Editor: This is the code after I read the answer. Now I don't need an intermediate map: I just need a map to map a string to a methodhandle:

@ParametersAreNonnullByDefault
public class PathMatcherProvider
{
    private static final MethodHandles.Lookup LOOKUP
        = MethodHandles.publicLookup();
    private static final MethodType CONSTRUCTOR_TYPE
        = MethodType.methodType(void.class,final String arg)
    {
        Objects.requireNonNull(name);
        Objects.requireNonNull(arg);

        final MethodHandle handle = handleMap.get(name);
        if (handle == null)
            throw new UnsupportedOperationException();

        try {
            return (PathMatcher) handle.invokeExact(arg);
        } catch (Throwable throwable) {
            throw new RuntimeException("Unhandled exception",@Nonnull final Class<? extends PathMatcher> matcherClass)
    {
        Objects.requireNonNull(name);
        Objects.requireNonNull(matcherClass);

        final MethodHandle handle;
        final MethodType type;

        try {
            handle = LOOKUP.findConstructor(matcherClass,CONSTRUCTOR_TYPE);
            type = handle.type().changeReturnType(PathMatcher.class);
            handleMap.put(name,handle.asType(type));
        } catch (NoSuchMethodException | illegalaccessexception e) {
            throw new RuntimeException("cannot find constructor",e);
        }
    }
}

Solution

When the compiler makes an invokeexact call, it records the object as the expected return type From methodhandle Javadoc (emphasize my):

At runtime, the method handle actually returned regexpathmatcher, so invokeexact failed with the wrong methodtypeexception

You need to explicitly specify the return type using (compile time) conversion:

return (RegexPathMatcher)handleMap.get(c).invokeExact(arg);

In addition to being implemented through different pathmatchers, you need to convert the method handle to return the pathmatcher using astype, and then use the pathmatcher as the expected return type

//in findConstructor
MethodHandle h = LOOKUP.findConstructor(matcherClass,CONSTRUCTOR_TYPE);
return h.asType(h.type().changeReturnType(PathMatcher.class));

//in getPathMatcher
return (PathMatcher)handleMap.get(c).invokeExact(arg);
The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>