开发者

Casting of interfaces

interfaces provide a useful abstraction capability. One can have a class Foo implement some interfaces, say A, B, and C. Some client code may get a reference of type A, others one of type B, etc. each actually the same Foo object but the interface exposing only a narrow subset of the functionality. Of course, evil client code can try to cast the A reference to Foo, then access the other functionality.How to prevent t开发者_JS百科his?


This is called a "malicious cast" and you can prevent it by having a wrapper that implements only the narrow interface you want to expose (by delegating to a private reference to the object that you would have otherwise directly passed to the evil client).

However, if the client is not only evil, but powerful as well, he might be able to use reflection to get to the hidden reference anyway.


Normal inheritance will always allow it, you can do nothing with it. If you want to expose some class as interface but hide other methods use Adapter pattern (google it)


You can't. One workaround is to implement three proxy classes, one to implement each interface, that forward all calls to a single Foo instance.


The person who performs a malicious cast does so at their own risk. In almost all cases, you can safely assume that the user will not use an object in a manner outside the specified interface contract.

The only time you really need to use a proxy object is if you are exposing security-sensitive object to untrusted code. Otherwise, spend your time making clear documentation about how objects can be used and work under the assumption that it will be followed.


Hide the underlying object.

Let's say you have:

public interface A {
}

public class B implements A {
}

So, interface A implements just a subset of B's functionality. Effectively it hides parts of B. Your question is how to stop the user from downcasting A to a B.

B objectOfTypeB = (B)objectOfTypeA; // you don't want this

So, don't give the user access to class B. If the user can't import it, he can't instantiate it or downcast to it. So, he's force to use the interface and nothing more.

Change the above code to:

/* Publicly accessable interface */
public interface A {
}

/* Class hidden inside the package. */
public class B implements A {
}

Then, you can just have a function return an A, secure in the knowledge that the user can't use B.

/* Function that returns an A. */
public A foo() {
    /* ... */
    return objectOfTypeB;
}


You can use a Facade class.

This class should wrap a delegate of class Foo and then only expose interface methods of, say A and just forward them to the delegate.

On the other hand, you can prevent casting to Foo by declaring it package private and have a public factory method that returns just the interface A ( which in reality is Foo ). That way casting from other packages will not be possible ( still, somebody may play tricks with reflection ).


There is no really practical, non-invasive way to protect against this.

However, if your situation really requires this protection, use this utility class to create dynamic proxy (delegate) classes (adapted from Dynamic Proxy Classes - <50 lines of production code!!).

This will cause ClassCastExceptions at runtime if someone uses tries a malicious cast. You could even conditionalize the code to turn it off at production time (have newInstance() just return obj - the object to as the "proxy").

DynamicProxy.java

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class DynamicProxy implements java.lang.reflect.InvocationHandler {

    private Object obj;

    public static Object newInstance(Object obj, Class<?>... interfaces) {
        if (interfaces == null || interfaces.length == 0) {
            throw new IllegalArgumentException("No interfaces");
        }
        return java.lang.reflect.Proxy.newProxyInstance(
            obj.getClass().getClassLoader(),
            interfaces,
            new DynamicProxy(obj));
    }

    private DynamicProxy(Object obj) {
        this.obj = obj;
    }

    public Object invoke(Object proxy, Method m, Object[] args)
    throws Throwable
    {
        Object result;
        try {
            result = m.invoke(obj, args);
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        } catch (Exception e) {
            throw new RuntimeException("unexpected invocation exception: " +
                           e.getMessage());
        }
        return result;
    }

    // ** DEMO CODE BELOW HERE **

    interface A {
        void methodA();
    }

    interface B {
        void methodB();
    }

    static class Foo implements A, B {
        public void methodA() { System.out.println("A"); }
        public void methodB() { System.out.println("B"); }
    }

    public static void main(String[] args) {

        Foo foo = new Foo();  // implements both interfaces

        // calls foo's methods, but only A methods
        A a = (A) DynamicProxy.newInstance(foo, A.class);

        // calls foo's methods, but only B methods
        B b = (B) DynamicProxy.newInstance(foo, B.class);

        // calls foo's methods, but only B methods
        A ab = (A) DynamicProxy.newInstance(foo, A.class, B.class);

        a.methodA();
        b.methodB();
        ab.methodA();
        ((B) ab).methodB();

        // ClassCastException: $Proxy0 cannot be cast to DynamicProxy$Foo
        ((Foo) a).methodA();

        // ClassCastException: $Proxy1 cannot be cast to DynamicProxy$Foo
        ((Foo) b).methodB();

        // ClassCastException: $Proxy0 cannot be cast to DynamicProxy$B
        ((B) a).methodB();

        // ClassCastException: $DynamicProxy1 cannot be cast to DynamicProxy$A
        ((A) b).methodA();
    }
}
0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜