gpt4 book ai didi

"Cannot cast [Ljava.lang.Object;" passing Object[] to Object... varargs(“无法强制转换[Ljava.lang.Object;”正在将对象[]传递给对象...可变参数)

转载 作者:bug小助手 更新时间:2023-10-28 11:39:04 27 4
gpt4 key购买 nike



I'm struggling with a simple, fundamental Java language question that isn't making sense. I'm passing an Object[] to a method which expects varargs Object..., but the method seems to be interpreting the request as new Object[]{Object[]}. That is, instead of considering the Object[] as equivalent to Object..., it seems to be passing the Object[] as the first element of the varargs, thus wrapping the Object[] in another array of objects.

我正在努力解决一个简单的、基本的Java语言问题,这个问题没有任何意义。我将对象[]传递给一个方法,该方法需要varargs对象...,但该方法似乎将请求解释为新对象[]{对象[]}。也就是说,它似乎将对象[]作为varargs的第一个元素传递,而不是将对象[]等同于对象...,从而将对象[]包装在另一个对象数组中。


I have the following in Java 17:

在Java 17中有以下几点:


FooImpl foo = goGetFooImpl();
List<?> methodArgs = List.of(); //no method args
Object[] invokeArgs = Stream.concat(Stream.of(foo, methodArgs.stream()).toArray();
// this gives me the equivalent of `new Object[]{foo}`
// logging `List.of(invokeArgs)` shows `[com.example.FooImpl@…]`
MethodHandle barMethodHandle = goLookUpMethodHandle(foo, "bar");
methodHandle.invoke(invokeArgs); //expects varargs `foo, arg1, arg2, …`

I get:

我得到:



java.lang.ClassCastException: Cannot cast [Ljava.lang.Object; to com.example.FooImpl



The method signature I'm calling is MethodHandle.invoke(Object... args). It expects varargs, with the first object referencing the target object on which the method is being invoked. So this works:

我正在调用的方法签名是MethodHandle.Invoke(Object...Args)。它需要varargs,第一个对象引用正在调用该方法的目标对象。因此,这是可行的:


FooImpl foo = goGetFooImpl();
MethodHandle barMethodHandle = goLookUpMethodHandle(foo, "bar");
barMethodHandle.invoke(foo);

In this case I don't know ahead of time that there are no further arguments (i.e. methodArgs above might not be empty), which is why I combined everything into an Object[]. But even manually testing like this doesn't work:

在这种情况下,我事先不知道没有更多的参数(例如,上面的方法参数可能不是空的),这就是为什么我将所有内容组合到一个对象[]中。但即使是像这样的手动测试也不起作用:


FooImpl foo = goGetFooImpl();
MethodHandle barMethodHandle = goLookUpMethodHandle(foo, "bar");
barMethodHandle.invoke(new Object[]{foo});

The reason this is confusing me is that this is such a basic Java issue (a newbie question, really), and I have understood for years that Java would by default consider passing Object[] to a method expecting Object... to be equivalent. I had believed that I would have to cast Object[] to Object using (Object)invokeArgs in order for it to be considered the first object of the Object... (which I don't want). But this seems to be happening anyway without the cast.

这让我感到困惑的原因是这是一个非常基本的Java问题(真的是一个新手问题),多年来我一直明白,Java在默认情况下会考虑将Object[]传递给需要Object的方法...等同于。我曾认为,我必须使用(对象)invkeArgs将Object[]强制转换为Object,才能将其视为对象的第一个对象……(这是我不想要的)但在没有演员阵容的情况下,这似乎无论如何都会发生。


The error message seems to be telling me that Java is considering Object[] to be the first Object of the Object... varargs, because the method seems to be trying to convert the invokeArgs itself (which is a Object[]) to a reference to FooImpl, which is what is requested as the first argument in the varargs.

错误消息似乎告诉我,Java正在将Object[]视为该对象的第一个对象...Varargs,因为该方法似乎试图将invkeArgs本身(它是一个Object[])转换为对FooImpl的引用,而FooImpl是varargs中请求的第一个参数。


This doesn't make sense to me. Either I've misunderstood for years how varargs works, or something weird is going on with the MethodHandle.invoke(…) method specifically, or I'm just having a mental blank.

这对我来说没什么意义。要么是我多年来误解了varargs的工作原理,要么是方法处理调用(…)发生了一些奇怪的事情具体的方法,否则我的脑海里就一片空白。


Even if MethodHandle.invoke(…) has some sort of special behavior, aren't the arguments determined at compile time? How could MethodHandle.invoke(…) have different behavior based upon whether I use invoke(foo) or invoke(new Object[]{foo})? During compilation aren't they equivalent, and in either case the compiler will wind up passing new Object[]{foo} to the method, even in the case of invoke(foo)?

即使方法句柄.Invoke(…)具有某种特殊的行为,参数不是在编译时确定的吗?Invoke(…)根据我是使用Invoke(Foo)还是Invoke(new Object[]{foo}),有不同的行为?在编译过程中,它们不是等价的吗?在任何一种情况下,编译器都会将新对象[]{foo}传递给方法,即使是在Invoke(Foo)的情况下也是如此?


更多回答

This is not remotely a newbie question because MethodHandle.invoke works differently from literally any other method in Java. Its rules are different and complicated, and it is quite likely "something weird is going on with it specifically." Have you tried invokeWithArguments instead?

这根本不是一个新手问题,因为MethodHandle.Invoke的工作方式与Java中的任何其他方法都不同。它的规则是不同的,也是复杂的,很可能“它特别地发生了一些奇怪的事情”。您是否尝试过使用参数调用?

I understand that MethodHandle.invoke(…) works in strange and wondrous ways inside it, but I don't understand how that would affect the compiler's interpretation of how arguments are passed to it. I don't see how the internals of MethodHandle.invoke(…) could have any bearing on compiler method matching and interpretation of parameters—isn't that determined at compile time? I haven't looked at invokeWithArguments(…); I'll go check that out now, thanks.

我知道方法句柄.调用(…)在它内部以奇怪和奇妙的方式工作,但我不明白这会如何影响编译器对如何将参数传递给它的解释。我不明白MethodHandle.Invoke(…)的内部可能对参数的编译器方法匹配和解释有任何影响--这不是在编译时确定的吗?我还没有看过带参数的InvokeWith(…);我现在就去查一下,谢谢。

Put another way, how could MethodHandle.invoke(…) have different behavior based upon whether I use invoke(foo) or invoke(new Object[]{foo})? During compilation aren't they equivalent, and in either case the compiler will wind up passing new Object[]{foo} to the method, even in the case of invoke(foo)?

换句话说,方法句柄.Invoke(…)根据我是使用Invoke(Foo)还是Invoke(new Object[]{foo}),有不同的行为?在编译过程中,它们不是等价的吗?在任何一种情况下,编译器都会将新对象[]{foo}传递给方法,即使是在Invoke(Foo)的情况下也是如此?

@GarretWilson, there is a typo in your first code example, "... String List<?> methodArgs = List.of(); //no method args ...". And, you are using a concat method on a Stream#of call. Did you mean to use Stream#concat instead?

@GarretWilson,在您的第一个代码示例中有一个拼写错误,“...字符串列表<?>方法参数=List.of();//没有方法参数...”。而且,您在调用的Stream#上使用了Conat方法。您的意思是改用Stream#Concat吗?

"During compilation aren't they equivalent, and in either case the compiler will wind up passing new Object[]{foo} to the method, even in the case of invoke(foo)? " - No. MethodHandles.invoke is a @SignaturePolymorphic method, where there is a difference.

“在编译过程中,它们不是等价的吗?在任何一种情况下,编译器都会将新对象[]{foo}传递给方法,即使是在Invoke(Foo)的情况下也是如此?”-否。Invoke是一种@SignaturePolyphic方法,其中存在差异。

优秀答案推荐


or something weird is going on with the MethodHandle.invoke(…) method specifically



Yes! The signature of MethodHandle::invoke is a lie. It is not a Object... varargs method, and it does not return Object either. Both the formal parameters and the return type are placeholders. This is because MethodHandle::invoke is a signature polymorphic method.

是!方法句柄::调用的签名是一个谎言。它不是物体..。Varargs方法,它也不返回Object。形参和返回类型都是占位符。这是因为MethodHandle::Invoke是一种签名多态方法。


See section 15.12.3 of the Java language spec (se20). Quoted here, I've omitted the irrelevant parts:

请参阅Java语言规范(SE20)的15.12.3节。引用如下,我省略了不相关的部分:



A method is signature polymorphic if all of the following are true:



  • It is declared in the java.lang.invoke.MethodHandle class or the java.lang.invoke.VarHandle class.



  • It has a single variable arity parameter (§8.4.1) whose declared type is Object[].



  • It is native.





This affects how the he compile-time parameter types and compile-time result are determined:

这会影响如何确定编译时参数类型和编译时结果:




  • If the compile-time declaration for the method invocation is a signature polymorphic method, then:



    • The compile-time parameter types are the types of the actual argument expressions. ...



    • The compile-time result is determined as follows:


      ...



      • Otherwise, if the method invocation expression is an expression statement, the compile-time result is void.







(See also the section of the MethodHandle javadoc on signature polymorphism)

(另请参阅关于签名多态的MethodHandle javadoc部分)


In your case the type of the single actual argument expression is Object[], so the type of the invocation is (Object[])void.

在您的例子中,单个实际参数表达式的类型是Object[],因此调用的类型是(Object[])void。


The invocation then fails because the implementation of invoke tries to automatically adapt the argument type to the parameter type of the method handle, which is com.example.FooImpl. For reference types this is done using a cast, as explained in the documentation of MethodHandle::asType. Since Object[] can not be cast to FooImpl, you get the ClassCastException.

然后调用失败,因为Invoke的实现试图自动使参数类型适应方法句柄的参数类型,即com.example.FooImpl。对于引用类型,这是使用强制转换完成的,如方法句柄::asType的文档中所述。由于Object[]不能强制转换为FooImpl,因此会出现ClassCastException异常。


As @Louis Wasserman says in the comments: if you want the Object[] to be expanded into a list of arguments you need to use MethodHandle::invokeWithArguments.

正如@Louis Wasserman在评论中所说:如果希望将Object[]扩展为参数列表,则需要使用MethodHandle::InvokeWithArguments。


更多回答

"The signature of MethodHandle::invoke is a lie. It is not a Object... varargs method … [T]he Object[] you pass … is not expanded into a list of arguments like what would normally happen …." 😮 I'm speechless. Wow. Thanks for that clarification. I'll go read up on all this. Otherwise, I don't know what to say. That was totally unexpected!

“😮::Invoke的签名是一个谎言。它不是一个对象...…方法…[T]你传递的对象[]不会像通常发生的那样扩展成一个参数列表。”…我说不出话来。哇。谢谢你的澄清。我要去读一读这一切。否则,我不知道该说什么。这完全是出乎意料的!

So, the PolymorphicSignature is a culprit here - it indicates that a method can have different signatures depending on the context.

因此,多态签名在这里是罪魁祸首-它表明一个方法可以根据上下文具有不同的签名。

@GarretWilson The main advantage of signature polymorphism is, for one, that if you pass a primitive argument, it will not have to be boxed into an object (e.g. no need to convert int to Integer). But also: it allows the runtime to generate the implementation of the method on demand, based on the specific type of the invocation. This means that a sigpoly method can have many different implementations, all with a different type. Where each implementation is specialized for exactly that type. Both these things are very useful features for maximizing performance.

@GarretWilson签名多态的主要优点之一是,如果传递原始参数,则不必将其装箱到对象中(例如,不需要将int转换为Integer)。而且:它还允许运行库根据调用的特定类型按需生成方法的实现。这意味着一个sigpoly方法可以有许多不同的实现,所有实现都具有不同的类型。其中每个实现都专门针对该类型。这两件事对于最大化性能来说都是非常有用的功能。

@GarretWilson invokeExact matches up the types of the argument expressions with the type of the method handle. If they don't match, an exception is thrown. But, if you pass the arguments in a list using invokeWithArguments, there are no argument expressions besides the List. So there is nothing to check against.

@GarretWilson invkeExact将参数表达式的类型与方法句柄的类型进行匹配。如果它们不匹配,则抛出异常。但是,如果使用InvokeWithArguments传递列表中的参数,则除了列表之外,没有参数表达式。因此,没有什么需要检查的。

It is absolutely special and extralinguistic, but it exists in the language to be the basis for a number of important use cases for which the overhead of boxing isn't acceptable.

它绝对是特殊的和非语言的,但它存在于语言中,是许多重要用例的基础,对于这些用例,拳击的开销是不可接受的。

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