【问题标题】:Can a Java interface in another package be refactored from my code?可以从我的代码重构另一个包中的 Java 接口吗?
【发布时间】:2011-10-22 15:52:05
【问题描述】:

我在我的 Java 项目中使用(开源)API 并从中引用接口。一个接口 (Foo) 在 API 和使用它的项目中都被广泛使用。对于我的项目,我想使用一个接口,它只公开Foo 所做的方法的子集,这样Foo 将继承我的新接口(@98​​7654324@)的方法。我根本不想改变Foo的界面;相反,我希望能够编写实现Bar 的类,然后将它们与实现Foo 的类一起操作,就好像它们都是Bar 一样。

由于 API 位于不同的包中,是否有任何方法可以使 Bar 成为所有 Foo 的超类(接口)?

【问题讨论】:

    标签: java oop inheritance interface package


    【解决方案1】:

    您可能需要使用Aspect 来实现这一点。看起来 AspectJ 有一个 @DeclareParents 可能会做你想做的事。

    【讨论】:

    • 如果我理解正确的话,这会在运行中创建代表,基本上充当适配器。我还能将BarImpl 类写成实现Bar 并且只将@DeclareParents(或@DeclareMixin)应用于Foo 吗?我看到的一些示例似乎避免使BarImpl 成为Bar 的实际实现。
    • 嗯,不确定,以前没有为此目的使用过 Aspects。你所说的让我怀疑这是一个本垒打的解决方案。手动适配器可能是一种更可靠且可维护的方式。
    【解决方案2】:

    我希望能够编写实现 Bar 的类,然后 操纵那些以及实现 Foo 的类,就好像它们是 所有酒吧。

    您可以创建一个实现 Foo 的抽象 Bar。您将放弃两者共享的方法并实现 Bar 不共享的方法,因此它们会引发类似 UnsupportedOperationException 的异常。

    但是你不能像操作 Bar 一样操作 Foo;这是相反的方式。您无法按照您提出的方式满足 Liskov 替换原则。

    【讨论】:

    • 我认为从Foo 的实现中抛出UnsupportedOperationException 也不满足LSP,通常应该避免。虽然我知道有先例,但在某些情况下它可能是正确的选择。
    • 同意,从用户的角度来看并不令人满意,但是为了编译器这是必须的。
    【解决方案3】:

    您可以使用Adapter Pattern 来完成此操作:

    public class FooAdapter implements Bar {
       private Foo wrapped;
       public FooAdapter(Foo foo) {
          wrapped = foo;
       }
       public void op1() {
          foo.op1();
       }
       public string op2(int param) {
          return foo.op2(param);
       }
    }
    

    【讨论】:

      【解决方案4】:

      因为 Java 具有标称类型,因此 org.theirs.Fooorg.yours.Foo 没有任何关系,即使它们具有完全相同的方法签名,我认为这与 Java 中的继承无关。即使使用泛型,AFAIK 也无法说“此方法采用 Bar<T extends Foo OR Bar> 的实例”。

      相反,我认为您想为库的Foo 使用Adapter interface,并使用您自己的(即FooAdapter,与Foo 具有完全相同的签名)并扩展Bar。然后可以从FooAdapter 中提取您想要在Bar 中的方法。不幸的是,您的代码必须进行修改,以便在任何地方您之前都引用了他们的Foo,而您将改为引用:

      • Bar 如果调用的唯一方法是您将在界面中定义的方法
      • FooAdapter 如果在 Foo 中定义但不在 Bar 中定义的方法被调用。

      这种方法虽然干净,并且很好地分离了关注点,但恐怕实施起来会很痛苦且乏味。

      下面的代码 sn-ps 显示了如何工作的示例:

      第三方库

      package org.theirs;
      public interface Foo {
          void doSomething();
          void doSomethingExtra();
      }
      

      您的代码

      package org.mine;
      public interface Bar {
          void doSomething();
      }
      
      public class BarImpl implements Bar{
           public void doSomething( /* implementation */ );
      }
      
      public class FooAdapter implements Bar{
          private final Foo adapted;
          public FooAdapter(Foo adapted) {
               this.adapted = adapted;
          }
          public void doSomething() {
              adapted.doSomething(); // delegate to adapted instance
          }
      }
      
      public class UsingThoseBars {
          public void doSomethingWithAllThoseBars(Collection<Bar> bars) {
               // each entry in bars could either be a BarImpl or a FooAdapter    
          }
      }
      

      您可以从这个示例中看到方法 doSomethingExtra() 无法从您的代码中访问,因为接口 Bar 没有指定它。

      其他建议

      请注意,您可以使用类重写来做一些有用的技巧,例如使用 AspectJ。我假设您希望在编译时使用纯 Java 获得您想要的效果。

      另一个建议是实现Bar,它会为您不需要的Foo 方法抛出UnsupportedOperationException。尽管在主要的 Java 库中有这样的先例(例如 JDK 中的UnmodifiableList),但我一般建议不要使用这种做法。但是,如果用您的新 FooAdapter 替换对他们的 Foo 的引用的成本非常高,那么使用此策略可能是一个很好的折衷方案。

      【讨论】:

        猜你喜欢
        • 2019-08-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-12-01
        • 2013-03-28
        • 2011-01-10
        相关资源
        最近更新 更多