开发者

Compiler warning when casting to generic method type variable in Java

开发者 https://www.devze.com 2023-01-24 08:54 出处:网络
Edit: I initially accepted thejh\'s answer, but I wasn\'t really satisfied with it since I wanted to make proper use of generics. So, I kept doing research and found a solution. Read about it in my an

Edit: I initially accepted thejh's answer, but I wasn't really satisfied with it since I wanted to make proper use of generics. So, I kept doing research and found a solution. Read about it in my answer below.


Here's a little self-contained piece of Java code which shows what I'm trying to do. It compiles, runs, and behaves correctly.

 1 import java.lang.reflect.Method;
 2 import java.lang.reflect.InvocationTargetException;
 3 
 4 public class Example
 5 {
 6    public static <T> void foo(Method method, String target, Object argument, T expectedReturn) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
 7    {
 8       T actualReturn = (T) method.invoke(target, argument);
 9       System.out.print(actualReturn.equals(expectedReturn));开发者_开发技巧
10    }
11    
12    public static void main(String[ ] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
13    {
14       foo(String.class.getMethod("charAt", int.class), "test", 1, 'e');
15    }
16 }

Running this prints true to the console, which is what I expected. What's bothering me is that, due to the cast on line 8, I'm getting a warning when I compile it, as follows (jGRASP is my IDE, by the way).

----jGRASP exec: javac -g -Xlint:unchecked Sandbox.java

Sandbox.java:8: warning: [unchecked] unchecked cast

found : java.lang.Object

required: T

1 warning

----jGRASP: operation complete.

Originally, I tried line 8 without the cast, but that failed to compile with an error complaining about finding an Object when it required T (invoke returns an Object). Later on, I rewrote it like this, blindly hoping to get rid of the warning.

T actualReturn = method.getReturnType( ).cast(method.invoke(target, argument));

But that gives a compile error that I can't make head nor tail of.

----jGRASP exec: javac -g -Xlint:unchecked Sandbox.java

Sandbox.java:8: incompatible types

found : capture#898 of ?

required: T

1 error

----jGRASP wedge: exit code for process is 1.

----jGRASP: operation complete.

And that number next to capture# is different each time I try to compile with that same line of code.

So, what exactly is the problem? Why am I getting the warning when I cast the object returned by invoke to the type variable? Does that indicate that I'm doing something wrong? How can I write this so that the warning goes away? And I'd prefer not to suppress it with an annotation, as that doesn't seem like much of a solution to me.


I looked into this some more and found that I could solve the problem by using class literals as runtime type tokens, as discussed in the Java Tutorials.

I had the right idea with method.getReturnType( ).cast(...), but it didn't work because the return type of getReturnType( ) is Class<?>, and I needed Class<T>.

So, here's what the method looks like now.

public static <T> void foo(Class<T> returnType, Method method, String target, Object argument, T expectedReturn) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
{
    T actualReturn = returnType.cast(method.invoke(target, argument));
    System.out.print(actualReturn.equals(expectedReturn));
}

And here's a sample call.

foo(Character.class, String.class.getMethod("charAt", int.class), "test", 1, 'e');

That compiles without warnings and prints true to the console. Note that if you're expecting the underlying method to return a primitive, the returnType parameter needs to be its respective wrapper class.


Why do you cast it to T? Why don't you do it this way?

Object actualReturn = method.invoke(target, argument);
System.out.print(actualReturn.equals(expectedReturn));

Oh, and in case the method could return null and expectedReturn isn't, this is better:

Object actualReturn = method.invoke(target, argument);
System.out.print(expectedReturn.equals(actualReturn));


So, what exactly is the problem? Why am I getting the warning when I cast the object returned by invoke to the type variable?

Method can return any Object. And it can't be cast to T in all cases.

Does that indicate that I'm doing something wrong?

it's bad practice to mix reflection and generics, imho.

How can I write this so that the warning goes away?

Personally, I think you can't avoid this warning without refactoring.

0

精彩评论

暂无评论...
验证码 换一张
取 消