代码之家  ›  专栏  ›  技术社区  ›  Srinivas Reddy Thatiparthy

接口的铸造

  •  4
  • Srinivas Reddy Thatiparthy  · 技术社区  · 16 年前

    接口提供了一种有用的抽象能力。可以让一个类foo实现一些接口,比如a、b和c。一些客户机代码可以获得类型a的引用,其他的是类型b的引用等。每个对象实际上都是同一个foo对象,但是接口只公开了功能的一个子集。当然,邪恶的客户端代码可以尝试将a引用强制转换为foo,然后访问其他功能。如何防止这种情况发生?

    7 回复  |  直到 16 年前
        1
  •  10
  •   Thilo    16 年前

    这被称为“恶意强制转换”,您可以通过一个只实现要公开的狭窄接口的包装器(通过将私有引用委托给对象,否则将直接传递给邪恶的客户机)来阻止它。

    然而,如果客户不仅是邪恶的,而且是强大的,他可能会使用反射来获得隐藏的参考。

        2
  •  2
  •   Andrey    16 年前

    正常继承总是允许的,你不能用它做任何事情。如果要将某些类作为接口公开,但隐藏其他方法,请使用 Adapter 模式(谷歌IT)

        3
  •  1
  •   Marcelo Cantos    16 年前

    不能。一个解决方法是实现三个代理类,一个用于实现每个接口,将所有调用转发到单个foo实例。

        4
  •  1
  •   Sam Harwell    16 年前

    执行恶意强制转换的人会自行承担风险。在几乎所有情况下,您都可以安全地假定用户不会以指定接口约定之外的方式使用对象。

    您真正需要使用代理对象的唯一时间是将安全敏感对象公开给不受信任的代码。否则,就要花时间弄清楚文档,说明如何在遵循对象的前提下使用和工作对象。

        5
  •  0
  •   scvalex    16 年前

    隐藏基础对象。

    假设你有:

    public interface A {
    }
    
    public class B implements A {
    }
    

    因此,接口A只实现了B功能的一个子集。实际上,它隐藏了B的某些部分。您的问题是如何阻止用户将A向下转换为B。

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

    所以, 不允许用户访问B类 . 如果用户不能导入它,他就不能将其实例化或向下转换。所以,他是使用界面的力量,而不是别的。

    将上述代码更改为:

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

    然后,您可以让一个函数返回一个a,安全地知道用户不能使用b。

    /* Function that returns an A. */
    public A foo() {
        /* ... */
        return objectOfTypeB;
    }
    
        6
  •  0
  •   Alexander Pogrebnyak    16 年前

    您可以使用Facade类。

    这个类应该包装一个类foo的委托,然后只公开的接口方法,比如a,并将它们转发给委托。

    另一方面,您可以通过声明包私有来防止强制转换到foo,并且拥有一个只返回接口a(实际上是foo)的公共工厂方法。这样就不可能从其他包中进行铸造(不过,有些人可能会玩反射的把戏)。

        7
  •  0
  •   Bert F    16 年前

    没有真正实用的、非侵入性的方法来防止这种情况发生。

    然而 ,如果您的情况确实需要这种保护,请使用该实用程序类创建动态代理(委托)类(改编自 Dynamic Proxy Classes -<50行生产代码!!!)

    这将导致 ClassCastException 如果有人使用,则在运行时尝试恶意强制转换。您甚至可以对代码进行条件化,以便在生产时关闭它(有 newInstance() 就回来吧 obj -对象作为“代理”)。

    动态氧气.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();
        }
    }