Java 8中的接口

13

J. Bloch在他的《Effective Java》一书中针对Java 6版本提到了以下内容(第17条):

如果您认为必须允许从这样一个类进行继承,那么一个合理的方法是确保该类从不调用任何可重写方法,并记录此事实。换句话说,完全消除类对可重写方法的自我使用。

第18条:

如果您使用抽象类来定义类型,则给想要添加功能的程序员留下的唯一选择就是使用继承。结果得到的类比包装类更弱且更脆弱。

虽然接口不允许包含方法实现,但使用接口来定义类型并不妨碍您为程序员提供实现协助。

现在在Java 8中,通过默认方法实现(使用接口中的其他方法)进行的接口继承是危险的。

例如:

public inteface MyInterface{

   public void compute(Object o);

   public default void computeAll(List<Object> oo){
         for(Object o: oo)
            compute(o);       //self-use
   }
}

据J. Bloch所说,尝试实现接口可能会带来一些问题,因为:

  1. 像这样覆盖方法(类似于J.Bloch提供的示例):

    public class MyInterfaceCounter implements MyInterface{
    
      private int count = 0;
    
      @Override
      public void compute(Object o) {
        count++;
      }
    
      @Override
      public void computeAll(List<Object> oo){
        count += oo.size();            //Damn!!
        MyInterface.super.computeAll(oo);
      }
    }
    
  2. 客户端访问接口的内部,即他们必须了解默认实现。



  3. 在Java 8中应该怎么做?Effective Java的规则仍然适用吗?

    此外,我们无法将默认方法声明为final(就像我们可以为类所做的那样),这会使自我使用对覆盖者来说不太危险。


3
过去的规则是否总是适用于未来的情况? - Kayaman
2
抱歉要这么说。我认为这是一个不错的问题,但可能过于主观,不太适合在SO上讨论。 - Mena
1
@Mena 也许吧,但我认为这个问题正是关于在实现Java8接口时要做的正确事情。我的第一个想法是永远不要使用默认方法... - St.Antario
6
J. Bloch 表示有一种方法是避免使用可覆盖方法。我认为 Java 8 的实现者们非常清楚默认方法的这个陷阱。这就是为什么他们在文档中不仅说明了默认方法的功能,还说明了它们是如何工作的。例如,请查看 Collection.removeIf() 方法,其中清楚地说明了该方法的实现方式。 - JB Nizet
1
基本上,接口的概念是声明将从其他对象访问的公共方法,因此定义公共方法以仅在类内部使用并允许其他人覆盖它是相当无用的,只需通过扩展它即可。出于这个原因,我们有带有私有方法的继承,或者您也可以将它们定义为公共最终方法。 - AntJavaDev
显示剩余9条评论
1个回答

3

好的,从您之前的问题中获取答案并看看我们可以应用什么:

你可以简单地避免自我使用。

在这种情况下,您不能这样做。 在实现该接口时,如果要提供默认实现,则您唯一依赖的选择是方法 compute 。 您必须使用它或根本不提供实现。

您可以使其中一个涉及的方法为final,以使其无法被覆盖。

在接口中也行不通。

您可以使类为final,以使其无法被扩展。

在接口中也行不通。

您可以在Javadoc注释中描述类的自我使用模式(满足让其他人知道的要求)。

这是此处留下的唯一选择。 要么记录它,要么不提供默认实现。 所以,基本思想仍然适用,但您的选择有些有限。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接