【问题标题】:implementing interfaces after the fact事后实现接口
【发布时间】:2010-10-19 14:46:53
【问题描述】:

我认为,以下不能在 Java 中完成。但我很乐意学习如何实现类似的东西。

假设我们有一个 C 类,它已经在编译代码中使用。 (我们既不能更改该代码也不能更改 C 的原始定义)。

进一步假设有一些有趣的代码可以重复使用,只要 C 实现接口 I。事实上,派生 D 或多或少是微不足道的,它只是 C + 接口方法的实现。

然而,似乎没有办法,一旦我有一个 C,就说:我希望你成为一个 D,即一个 C 实现 I。

(旁注:我认为如果 D 是 C 并且与 C 的唯一区别是添加的方法,则应该允许强制转换 (D)c,其中 c 的运行时类型是 C。这应该是安全的,不是吗? )

如何解决这场灾难?

(我知道工厂设计模式,但这似乎不是一个解决方案。因为,一旦我们设法在以前是 C 的所有地方创建 D,其他人发现另一个接口 J 有用并派生 E 扩展 C实现了 J。但是 E 和 D 是不兼容的,因为它们都向 C 添加了一组不同的方法。因此,虽然我们总是可以在期望 C 的地方传递 E,但我们不能在期望 D 的地方传递 E。而是,现在,我们需要一个新的类 F 扩展 C 实现 I,J。)

【问题讨论】:

    标签: java design-patterns oop interface


    【解决方案1】:

    您不能使用委托类,即包装“C 类”实例的新类,但也实现“接口 I”?

    public class D implements I {
    
        private C c;
    
        public D (C _c) {
            this.c = _c;
        }
    
        public void method_from_class_C() {
            c.method_from_class_C();
        }
        // repeat ad-nauseum for all of class C's public methods
        ...
    
        public void method_from_interface_I() {
            // does stuff
        }
        // and do the same for all of interface I's methods too
    }
    

    然后,如果您需要调用通常采用I 类型参数的函数,请执行以下操作:

    result = some_function(new D(c));
    

    【讨论】:

    • 为什么不简单地“D 类扩展 C 实现 I”?
    • 因为它要么要求您使用 'new D(...)' 而不是 'new C(...)' 进行构造,这可能是不可能的。即使 D 扩展了 C,也不可能执行“D d = (D)c”。不过,从 D 向下转换到 C 应该是可能的。
    • 我之前试图解释过。我从不受我控制的代码中获取 C,并希望将它们(不是新的/不同的对象)传递给与接口一起使用的代码。
    • 这就是委托的重点——是的,它确实创建了一个新的(临时)对象,但这是必要的,因为您不能将 C 转换为 D。
    • 和动态代理的东西具有相同的属性 - 你最终仍然会传递原始对象以外的其他东西,除此之外,它更难理解......
    【解决方案2】:

    如果你只需要兼容接口,那么没问题看看dynamic proxy classes,它基本上是你在java中运行时实现接口的方式。

    如果您需要与类类似的运行时兼容性,我建议您查看 cglib 或 javaassist 开源库。

    【讨论】:

    • 这看起来很复杂,但我一定会试一试。你能谈谈运行时成本吗,即看起来我需要每个对象一个额外的调用处理程序实例。
    • 委托方法会简单得多,这增加了复杂性,但对这个特定问题没有任何好处。
    • 我的想法是,除了将适配器模式专门应用于运行时解决方案之外,还需要另一种解决方案。可能是误解
    【解决方案3】:

    如果您(可以)管理加载您的类CClassLoader,那么您可以尝试使用字节码检测进行一些类加载时间恶作剧,以使类实现接口。

    当然,在构建时也可以这样做。这种方式甚至可能更容易(因为您不需要访问 ClassLoader)。

    【讨论】:

      【解决方案4】:

      我相信使用java.lang.reflect.Proxy 可以实现您想要的;事实上,我为当前的项目做了类似的事情。然而,这是一项相当多的工作,并且由此产生的“混合对象”可能会暴露出奇怪的行为(因为对它们的方法调用被路由到不同的具体对象,当这些方法尝试相互调用时会出现问题)。

      【讨论】:

        【解决方案5】:

        (旁白:我认为演员表(D)c, 其中c的运行时类型是C,应该是 如果 D 是 C 并且是唯一的,则允许 与 C 不同的是添加的方法。 这应该是安全的,不是吗?)

        一点也不。如果您可以进行此转换,那么您可以编译尝试调用此对象上的“添加方法”之一的代码,这将在运行时失败,因为该方法在 C 中不存在。

        我认为您在想象演员会检测 C 中“缺失”的方法并将它们自动委托给 D。我怀疑这是否可行,尽管我无法谈论语言设计的含义。

        在我看来,您的问题的解决方案是:

        定义类 D,它扩展了 C 并实现了 I
        定义一个构造函数 D(C c),它本质上将给定 C 对象的状态克隆到一个新的 D 对象中。
        D 对象可以传递给您现有的代码,因为它是 C,它可以传递给需要 I 的代码,因为它是 I

        【讨论】:

        • 我的意思是 (D)c 实际上将 c 的运行时类型更改为 D(例如,通过覆盖 vtable 指针)。当然,这只有在编译器能够证明从 C 到 D 的运行时强制转换不会改变对象的 Cish 行为,而只是添加新功能时才可能实现。
        • 通常只能向下转换(即从子类到其超类),因为这删除功能和数据成员。
        【解决方案6】:

        我认为你不能这样做,因为 Java 是严格类型的。我相信它可以在 Ruby 和 Python 等语言中使用 mixins 来完成。

        对于 Java,它绝对是 Adapter 设计模式的一个很好的用法(它之前已经被提议为“包装器”对象)。

        【讨论】:

          猜你喜欢
          • 2011-07-20
          • 1970-01-01
          • 1970-01-01
          • 2016-02-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-06-03
          相关资源
          最近更新 更多