Question

IMy question concerns the behavior of type casting with dynamic proxy and java generics. More specifically, I want to instrument a servelet object using dynamic proxy. For better reusability, I adopted some generic code template for creating proxy objects as below.

@SuppressWarnings("unchecked")
public static <T> T instrument(final T aT) {
    T proxy = (T) Proxy.newProxyInstance(aT.getClass().getClassLoader(), new Class[]{MyServelet.class}, new InvocationHandler() {
        @Override
        public Object invoke(Object aProxy, Method aMethod, Object[] aArgs) throws Throwable {
        ..... instrumentation logic goes here
        }
    });
     System.out.println("proxy class " + proxy.getClass());
     System.out.println("input class " + aT.getClass());
     return proxy;
}

Here T is my concrete servelet implementation class that implements MyServelet Interface. However, If I run the above method it prints out

proxy class class com.sun.proxy.$Proxy0
input class class MyServeletImplementation

So I wonder what happened to the type casting statement in the code snippet. It seems like it quietly failed since the proxy didn't get casted to MyServeletImplementation, but it didn't throw ClassCastException either. Can somebody shed me some light on this? Thanks!

Was it helpful?

Solution

No cast took place in the actual compiled bytecode of the method, because of type erasure.

When generating bytecode, the compiler treats any variable of a parameter type as having the same type as that parameter type's upper bound, or Object if the type is unbounded. So if T in your method had the constraint <T extends MyServelet>, the compiler would have treated proxy as a variable of type MyServelet, and inserted a cast. However, as T is unbounded, it's treated as a variable of type Object - thus, no cast.

OTHER TIPS

The point of the dynamic proxy is that you don't care about the actual class of the proxy that is being returned. You just care that it implements all desired and specified interfaces.

The Javadocs for newProxyInstance state:

Returns:

a proxy instance with the specified invocation handler of a proxy class that is defined by the specified class loader and that implements the specified interfaces

So, newProxyInstance returned an instance of com.sun.proxy.$Proxy0 (whatever that is, we don't really care), but it also made sure that it's a MyServelet. You should be able to cast it to MyServelet without a ClassCastException, because newProxyInstance created the class to implement that interface, and you specified that it should by passing in new Class[]{MyServelet.class}.

For type casting not to have failed, Java generics must have inferred that T is MyServelet, not MyServeletImplementation. Perhaps when you called instrument, you did so like this:

MyServelet proxy = YourClass.instrument(new MyServeletImplementation());

So the cast was to MyServelet which should succeed, and not to MyServeletImplementation, which should fail.

The reason you needed to add @SuppressWarnings("unchecked") is because the compiler was trying to warn you that you were doing something wrong. by adding the suppression, you told the compiler that you didn't care because you knew what you were doing. maybe you shouldn't have added the annotation if you didn't uderstand why you were getting the warning in the first place?

Hint: the generic cast T proxy = (T) gets turned into Object proxy = (Object) by the compiler. ("Type Erasure" as mentioned in another answer).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top