gpt4 book ai didi

java - 为什么我不能在这里调用 .invokeExact(),即使 MethodType 没问题?

转载 作者:塔克拉玛干 更新时间:2023-11-03 02:52:41 24 4
gpt4 key购买 nike

对于我的一个项目,我必须对构造函数进行动态调用。但由于这是 Java 7,我使用 java.lang.invoke 而不是“经典”反射 API。

代码:

@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");
}
}

好的,这有效。

我遇到的问题是这一行:

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

如果我用 invokeExact 替换 invoke,我得到这个堆栈跟踪:

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)

我不太明白。 GlobPathMatcherRegexPathMatcher 都使用带有 String 作为参数的单个构造函数,两者的 MethodType 是因此在 CONSTRUCTOR_TYPE 中定义了什么。如果不是这样,我无论如何也无法“获取”MethodHandle

但我得到了一个WrongMethodTypeException。为什么?


编辑:这是我阅读答案后的代码;现在我不需要中间映射:我只需要一个映射,将 String 映射到 MethodHandle:

@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, 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 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", throwable);
}
}

protected final void registerPathMatcher(@Nonnull final String name,
@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);
}
}
}

最佳答案

当编译器发出 invokeExact 调用时,它会将 Object 记录为预期的返回类型。来自 MethodHandle javadoc(强调我的):

As is usual with virtual methods, source-level calls to invokeExact and invoke compile to an invokevirtual instruction. More unusually, the compiler must record the actual argument types, and may not perform method invocation conversions on the arguments. Instead, it must push them on the stack according to their own unconverted types. The method handle object itself is pushed on the stack before the arguments. The compiler then calls the method handle with a symbolic type descriptor which describes the argument and return types.

To issue a complete symbolic type descriptor, the compiler must also determine the return type. This is based on a cast on the method invocation expression, if there is one, or else Object if the invocation is an expression or else void if the invocation is a statement. The cast may be to a primitive type (but not void).

在运行时,方法句柄实际上返回 RegexPathMatcher,因此 invokeExact 失败并出现 WrongMethodTypeException。

您需要使用(编译时)转换显式指定返回类型:

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

除非您需要在不同的 PathMatcher 实现上通用,因此您应该使用 asType 转换您的方法句柄以返回 PathMatcher,然后使用 PathMatcher 作为预期的返回类型进行调用。

//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);

关于java - 为什么我不能在这里调用 .invokeExact(),即使 MethodType 没问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27278314/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com