代码之家  ›  专栏  ›  技术社区  ›  N8g

Java接口实现对[关闭]

  •  15
  • N8g  · 技术社区  · 16 年前

    是否有一种首选的方法或样式来创建接口方法的默认实现?假设我有一个常用的接口,在90%的情况下,我想要的功能是相同的。

    下面是一个简单的例子:

    接口

    public interface StuffDoer{
        public abstract void doStuff();
    }
    

    public class ConcreteStuffDoer{
        public static void  doStuff(){
            dosomestuff...
        }
    }
    

    使用默认功能的具体实现

    public class MyClass implements StuffDoer{
        public void  doStuff(){
            ConcreteSuffDoer.doStuff();        
        } 
    }
    

    有更好的方法吗?

    编辑

    10 回复  |  直到 11 年前
        1
  •  25
  •   Yishai    16 年前

    这是我将采取的方法:

    public interface MyInterface {
    
          MyInterface DEFAULT = new MyDefaultImplementation();
    
          public static class MyDefaultImplemenation implements MyInterface {
          }
     }
    

    当然,MyDefaultImplementation可能需要是私有的,或者它自己的顶级类,这取决于什么是合理的。

    然后,可以在实现中包含以下内容:

     public class MyClass implements MyInterface {
            @Override
            public int someInterfaceMethod(String param) {
                 return DEFAULT.someInterfaceMethod(param);
            }
      }
    

    当然,只有在没有国家参与的情况下,上述方法才有效。

        2
  •  16
  •   Péter Török    16 年前

    您可以将接口转换为抽象类,并根据需要为方法提供默认实现。

    更新: 我明白了,多重继承结束了把接口变成抽象类的过程。。。在这种情况下,我也会和你一样。如果方法的默认实现不依赖于状态,那么它们的最佳位置确实是在静态实用程序类中。但是,如果涉及到状态,我会考虑对象组合,它甚至可能最终成为类似 Decorator

        3
  •  8
  •   Brandon    11 年前

    现在Java 8已经推出,这种模式更好了:

    public interface StuffDoer{
        default void doStuff() {
            dosomestuff...
        }
    }
    
    public class MyClass implements StuffDoer {
        // doStuff automatically defined
    }
    

    Lambda得到了所有的关注,但这是java8给我们公司带来的最实际的好处。当我们不再需要抽象类来实现默认的方法时,继承层次结构就变得简单多了。

        4
  •  1
  •   mkadunc    16 年前

    静态实现有两个问题:

    • 通过任何静态代码分析都找不到它们,比如列出所有的实现者,因为静态方法没有实现接口,所以不得不在javadoc中提到默认的实现

    • 有时需要在实现器中具有某种状态,而这实际上不能在静态方法中完成

        5
  •  1
  •   Timo Westkämper    16 年前

    我还将使用静态方法声明无状态功能的默认功能。

    对于有状态功能,我更喜欢组合而不是继承。通过组合和使用委托/适配器,您可以组合来自许多源的默认功能。

    例如

    public interface StuffDoer{
        void doStuff();
        void doOtherStuff();
    }
    
    public class MyStuffDoer implements StuffDoer{
        private final StuffDoer mixin;
        public  MyStuffDoer(StuffDoer mixin){
            this.mixin = mixin;
        }
        public void doStuff(){
            mixin.doStuff();
        }
        public void doOtherStuff(){
            mixin.doOtherStuff();
        }
    
    }
    
    public class MyStuffDoer2 implements StuffDoer{
        private final StuffDoer mixin1, mixin2;
        public  MyStuffDoer(StuffDoer mixin1, StuffDoer mixin2){
            this.mixin1 = mixin1;
            this.mixin2 = mixin2;
        }
        public void doStuff(){
            mixin1.doStuff();
        }
        public void doOtherStuff(){
            mixin2.doOtherStuff();
        }
    
    }
    

    实现多个接口也是这种方法扩展得更好的一种情况。

    public interface A{
        void doStuff();
    }
    
    public interface B{
        void doOtherStuff();
    }
    
    public class MyStuffDoer implements A, B{
        private final A mixin1;
        private final B mixin2;
        public  MyStuffDoer(A mixin1, B mixin2){
            this.mixin1 = mixin1;
            this.mixin2 = mixin2;
        }
        public void doStuff(){
            mixin1.doStuff();
        }
        public void doOtherStuff(){
            mixin2.doOtherStuff();
        }
    }
    

    抽象类不能这样做。我在一些项目中使用过这种组合方法,效果很好。

        6
  •  1
  •   Stephen P    16 年前

    apply(Collection<T>) 总是 循环遍历集合并调用接口方法 boolean include(T item) 决定是否保留或过滤掉这个项目。我的SearchFilterBase提供了apply()的实现,实现者只需编写include()逻辑。


    作为对N8g评论的回应,您可以对基或适配器进行子类化,但不是 必修的 子类——您可以自己从头开始实现接口。基类或适配器是为了方便而提供的,它实现了no-op方法,所以您不必这样做,或者在抽象基类(比如我的 SearchFilterBase 类)。与将接口转换为抽象类相比,它的优点是不强制从抽象类继承。

        7
  •  1
  •   Kru    16 年前

    大多数时候,我会这样说:

    public interface Something {
      public void doSomething();
    
      public static class Static {
        public static void doSomething(Something self) {
          // default implementation of doSomething()
          // the name of the method is often the same
          // the arguments might differ, so state can be passed
        }
      }
    
      public static abstract class Abstract implements Something {
        // the default abstract implementation
      }
    
      public static class Default extends Abstract {
        // the default implementation
        public void doSomething() {
          Static.doSomething(this);
        }
      }
    
      public static interface Builder extends Provider<Something> {
        // initializes the object
      }
    }
    

    我倾向于使用内部类,因为这些类确实是相关的,但是您也可以使用常规类。

    另外,您可能希望将实用程序类( Static )在一个单独的文件中,如果这些方法不仅仅涉及接口。


    new SomethingDecorator(new ArrayList<Object>()) instanceof List<?> 如果 SomethingDecorator 不实现列表的接口。 您可以使用反射API,特别是使用 Proxy .

    另一个好方法是适配器,正如PersicsB所指出的那样。

        8
  •  1
  •   Andy Thomas    16 年前

    Dependency injection 可以提供委托,并指定一个默认实现——可以在单元测试中重写。

    例如 Guice 框架为其默认实现支持接口上的注释,该注释可以由单元测试中的显式绑定覆盖。

    @ImplementedBy( ConcreteStuffDoer.class )
    public interface StuffDoer{
        void doStuff();
    }
    
    public class ConcreteStuffDoer implements StuffDoer {
        public void  doStuff(){ ... }
    }
    
    public class MyClass implements StuffDoer{
      @Inject StuffDoer m_delegate;
      public void  doStuff(){ m_delegate.doStuff(); }
    }
    

    new .

        9
  •  0
  •   PersicsB    16 年前

    在灵活性方面,组合胜过多重继承。与适配器模式(参见GOF书籍)一样,它显然允许我们根据调用方的请求调整(变形)类以适应不同的行为。看到了吗 IAdaptable instanceof 运算符比调用canAdapt(clazz类)消耗更少的CPU。但在现代JVM中,这可能是错误的,因为 使用接口比使用继承计算instanceof更复杂。

        10
  •  0
  •   ZXX    16 年前

    您可以重新检查是否有充分的理由不将您认为默认的实现合并到实现该接口的第一个抽象类中—您知道,对现实的适应:-)

    此外,您还可以在抽象类中创建辅助的、受保护的虚拟方法,接口派生方法将调用该抽象类,从而赋予您更多的控制权,即派生类不必替换这些方法并复制;粘贴90%的代码-它可以重写从主方法调用的方法。给你一定程度的行为遗传。

    推荐文章