0

I have an interface defined as follows:

public interface Cache {
}

Then an abstract class implementing the above:

public abstract class AbstractCache implements Cache {
}

Then a concrete class inheriting from above:

public class RealTimeCache extends AbstractCache {
}

Then another class defined as follows:

public class CacheProbe {
    public static <T> T probe(T base) {
        return (T) Proxy.newProxyInstance(
                base.getClass().getClassLoader(),
                new Class[]{Cache.class},
                new MethodCountInvocationHandler(base) // I am not mentioning this class as it's irrelevant
        );
    }
}

I have a class as follows which is using all of the above:

public class CacheLoader<T extends Cache> {
    public T load() {
        T result = getResult(...);
        CacheProbe x = new CacheProbe(result);
        return x.probe();
    }
}

Lastly, the lines causing the issue (located outside above classes):

final CacheLoader<RealTimeCache> cacheLoader = getNewLoader(); //Method of this method is irrelevant and unchangeable
RealTimeCache x = cacheLoader.load(); //This is the line which is causing a runtime issue

Problem is, at run time the following exception is thrown at the last line mentioned above:

java.lang.ClassCastException: com.sun.proxy.$Proxy57 cannot be cast to RealTimeCache

However I don't see how this is possible because the dynamic proxy class generated is based on Cache.

How do I fix this ?

Please note that I can only change CacheProbe class in order to fix this. Cache, AbstractCache, RealTimeCache, CacheLoader and those last two lines are unchangeable.

Ahmad
  • 12,886
  • 30
  • 93
  • 146
  • Do RealTimeCache implements Cache? – Marcos Vasconcelos Jun 29 '18 at 19:35
  • It extends AbstractCache, which in turn implements Cache – Ahmad Jun 29 '18 at 19:36
  • Anyhow you are trying to cast Cache to a more specific class, you have to use something such: new Class[]{RealTimeCache.class} on newInstance method. Reference: https://docs.oracle.com/javase/8/docs/technotes/guides/reflection/proxy.html OR: Use Cache x = cacheLoader.load() since it will return a Cache – Marcos Vasconcelos Jun 29 '18 at 19:38
  • `Proxy.newProxyInstance` takes an interface based on which it generates a dynamic proxy class, so I cannot provide `RealTimeCache` – Ahmad Jun 29 '18 at 19:40
  • So try: RealTimeCache x = (RealTimeCache) (Cache) cacheLoader.load(); since you are downcasting cache – Marcos Vasconcelos Jun 29 '18 at 19:41
  • I cannot change those two lines. I only have control over the `CacheProbe` class, and so can only change that unfortunately. – Ahmad Jun 29 '18 at 19:42
  • @MarcosVasconcelos, you cannot successfully cast a reference to a type that is not its own class or a supertype of that class. Attempting to do so would provoke the same `ClassCastException` at runtime that the OP already receives. – John Bollinger Jun 29 '18 at 19:47
  • @JohnBollinger yeah.. i just trying to simulate it efficiently – Marcos Vasconcelos Jun 29 '18 at 19:49
  • Possibly see https://stackoverflow.com/questions/5289393/casting-variables-in-java. As I said under [your question about this yesterday](https://stackoverflow.com/questions/51092082/how-to-use-parent-interface-return-type-when-using-extends), you need to actually change `CacheLoader`. There's no way to magically get an instance of `RealTimeCache` from the `probe` method, unless you do something like pass it a `Class`. (Except in this case, since you're making a proxy, it's actually *impossible* to get an instance of `RealTimeCache`, because it's not an interface.) – Radiodef Jun 29 '18 at 20:23

1 Answers1

1

However I don't see how this is possible because the dynamic proxy class generated is based on Cache.

Yes, the docs for java.lang.reflect.Proxy say

Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods.

(emphasis added)

Thus, you cannot use Proxy to create (an instance of) a subclass of an arbitrary class of your choice.

How do I fix this ?

You can create an ordinary subclass of RealTimeCache, and return an instance of that. Proxy is meant primarily to serve for interfaces that are not known until runtime, and in that case the only way to interact with them anyway is the interface type. That's not your scenario.

If necessary, you can implement such a subclass in terms of a MethodCountInvocationHandler, just as your proxy class uses, but I'm sure it would be easier to implement whatever tooling that is supposed to provide directly.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • Thanks for the explanation. Can you provide a code sample of what you meant in terms of the solution ? – Ahmad Jun 29 '18 at 19:54
  • I'm sorry, @Ahmad, but I don't follow what you hope to see demonstrated with an example. Once we take out `Proxy`, everything else in your question is very abstract. – John Bollinger Jun 29 '18 at 19:57