Java接口 – 实现对

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

我的第一直觉是用静态方法创建一个具体的类。 然后,当我想要默认function时,我会将function委托给静态方法。

这是一个简单的例子:

接口

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

具体实施方法

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

使用默认function的具体实现

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

这里有更好的方法吗?

编辑

在看到一些提议的解决方案后,我想我应该更清楚我的意图。 本质上我试图解决Java不允许多重inheritance。 另外要明确我不是要声明Java是否应该允许多重inheritance。 我只是在寻找为实现接口的类创建默认方法实现的最佳方法。

这是我要采取的方法:

 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); } } 

它比其他地方存在但未被接口引用的默认实现类更自我记录,并且最终更灵活。 有了这个,你可以做一些事情,比如在需要时将默认实现作为方法参数传递(你不能用静态方法)。

当然,以上只有在没有涉及国家的情况下才有效。

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

更新:我看到,多重inheritance关闭了将接口更改为抽象类…在这种情况下,我会像你一样做。 如果方法的默认实现不依赖于状态,那么它们的最佳位置确实在静态实用程序类中。 但是,如果涉及到状态,我会考虑对象组合,它甚至可能最终像装饰器一样 。

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

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

Lambda得到了所有人的关注,但这是Java 8对我们公司最切实的好处。 当我们不再需要使用抽象类来进行默认方法实现时,我们的inheritance层次结构变得更加简单。

静态实现有两个问题:

  • 您无法使用任何静态代码分析找到它们,例如列出所有实现者,因为静态方法没有实现接口,因此您不得不在javadoc中提及默认实现

  • 有时需要在实现者中有一些状态,这在静态方法中是不可能的

因此,我更喜欢使用一个允许inheritance(如果你不受单inheritance限制)和组合的具体类,并调用结果类* Peer,因为它通常与实现接口的主类一起使用。 对等体将实现接口,并且还可以引用主对象,以防它需要在主类的名称中触发事件。

我还使用静态方法来声明无状态function的默认function。

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

例如

 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(); } } 

对于简单的情况,inheritance也是可以的,但它并不是非常灵活。

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

 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(); } } 

你不能用抽象类来做这件事。 我在一些项目中使用了这种组合方法,并且它工作得非常好。

我或多或少地遵循了我最初从Swing学到的模式。 我有一个接口,然后我创建一个“基地”或“适配器”类。 对我来说,适配器通常会为所有接口方法执行无操作实现,以允许实现者只需编写所需的方法而忽略其他方法。 base将是一个抽象类,为某些接口方法提供方便的实现 – 希望是“最有可能”的实现。

例如,我有一个SearchFilter接口,除其他外,还有一个apply(Collection)方法。 该方法几乎总是遍历集合并调用接口方法boolean include(T item)来决定是否保留或过滤掉该项。 我的SearchFilterBase提供了作为apply()的实现,实现者只需要编写他们的include()逻辑。

当然,实现者可以自由地简单地实现整个接口,而不是从Base派生,这比将接口更改为抽象类更有优势,这迫使他们使用单inheritance(这是java的问题)。 util.Observable)


响应N8g的注释 – 您可以inheritance基类或适配器,但不需要子类 – 您可以从头开始实现接口。 提供基础或适配器是为了方便,实现无操作方法,因此您不必这样做,或在抽象基类(如我的SearchFilterBase类)中实现方便的通用function。 与将接口转换为抽象类相比,它的优势在于您不会强制从抽象类inheritance。

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

 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 { // initializes the object } } 

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

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


顺便说一句,装饰器的问题是它们隐藏了其他实现的接口。 换句话说,如果你有一个装饰器,那么以下是假new SomethingDecorator(new ArrayList()) instanceof List如果SomethingDecorator没有实现列表的接口。 您可以使用reflectionAPI工作,特别是使用代理 。

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

dependency injection可以提供委托,并指定默认实现 – 可以在unit testing中覆盖。

例如, Guice框架在接口上支持其默认实现的注释,可以通过unit testing中的显式绑定来覆盖。

 @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(); } } 

这确实引入了创建MyClass实例需要Guice方法而不是简单new

组合在灵活性方面胜过多重inheritance。 与适配器模式(参见GOF书籍)一样,它明确允许我们根据呼叫者请求将类(变形)调整为不同的行为。 请参阅Eclipse RCP中的IAdaptable接口。 基本上使用接口与直接使用Java语言内置的适配器模式相同。 但是使用自适应类比实现多个接口要好。 自适应类更灵活,但它们有一些CPU开销: instanceof运算符比调用canAdapt(Class clazz)消耗更少的CPU。 但是在现代JVM中,这可能是错误的,因为使用接口计算instanceof比使用inheritance计算instanceof更复杂。

如果它是你的任何函数:-)作为静态工作,那么你不需要委托它,因为这意味着它不需要“this” – 它是一个实用函数。

您可以重新检查是否真的有压倒性的理由不将您认为默认实现的任何内容合并到实现该接口的第一个抽象类中 – 您知道,适应现实:-)

此外,您可以在抽象类中创建辅助的受保护虚拟方法,您的接口派生方法将调用这些方法,您可以更好地控制它,即派生类不必替换方法并复制和粘贴90%的代码 – 它可以只是覆盖从main方法调用的方法。 给你一定程度的行为inheritance。