Java 8:虚拟扩展方法 vs 抽象类

30

我正在研究Java 8接口中的新虚拟扩展方法:

public interface MyInterface {
   default String myMethod() { 
      return "myImplementation"; 
   }
}

我明白他们允许接口随时间演变的目的,以及多重继承的部分,但它们看起来对我来说非常像抽象类。

如果要进行新的工作,在提供“接口”的实现方面,是优先选择抽象类还是扩展方法?这两种方法在概念上是否相等?


8
区分行为继承和状态继承是至关重要的。 - assylias
“default” 方法是什么?这篇文章(http://www.lambdafaq.org/what-are-default-methods/)详细阐述了它们的目的。它们的主要目的是使得那些编译于 Java 7 的代码能够向后兼容,并且不需要实现 Java 8 中添加的任何“函数式”方法。 - jason
5个回答

34

这样的构造的一个主要目的是保留向后兼容性。Java语言添加闭包是相当重大的改变,需要更新才能充分利用它的优势。例如,Java 8中的 Collection 将有诸如 forEach() 这样的方法,与lambda一起使用。简单地将此类方法添加到现有的 Collection 接口中是不可行的,因为它会破坏向后兼容性。我在Java 7中编写的实现Collection的类将不再编译,因为它缺少这些方法。因此,这些方法被引入了“默认”实现。如果您了解Scala,您会发现Java的 interface 变得更像Scala的 traits。

关于接口和抽象类,它们在Java 8中仍然是不同的。例如,在接口中仍然不能有构造函数。因此,从本质上讲,这两种方法并不“概念上等价”。抽象类更加有结构化,并且可以与状态相关联,而接口则不能。您应该根据程序的上下文选择使用哪种方法,就像在Java 7及以下版本中一样。


5
  1. 抽象类不能成为lambda表达式的根类,而带有虚拟扩展方法的接口可以。
  2. 抽象类可以拥有构造函数和成员变量,而接口则不行。我认为是可能的构造函数执行以及可能会抛出受检异常的原因,阻止了抽象类成为lambda表达式的根。

如果您想编写一个允许用户使用lambda表达式的API,应该使用接口。


5

抽象类具有状态(实例字段),以提供一些通用行为(方法)。


通常情况下,您不会看到没有状态的抽象类。

接口规定功能。它们旨在将行为声明为合同,而不是实现它。
因此,作为接口的一部分指定的任何方法都是“帮助器”方法--它们不影响实现。


1
java.util.AbstractCollection?抽象类没有状态是很正常的。从Java SE 8开始,抽象类可以拥有实例状态,而带有实现的接口则不能,这只是一种语言上的怪异现象。 - Tom Hawtin - tackline
1
@TomHawtin-tackline:但是如果你查看AbstractCollection的文档,它说:“此类提供了Collection接口的骨架实现,以最小化实现此接口所需的工作量。”换句话说,这只是一个方便功能,但它不需要出于任何特定的设计原因。相比之下,编写适用于所有集合的通用代码是必须的,因此Collection是必要的。 - user541686
我认为那并不相关。抽象类通常不保存状态是正确的。在Java SE 8中,我相信“Collection”将包含一些“集合接口的骨架实现”。 - Tom Hawtin - tackline
1
@TomHawtin-tackline: “抽象类通常不保存状态”… 你介意列举一些其他的这样的类吗? - user541686

0

抽象类和函数式接口之间的区别很多,就像普通接口和抽象类之间的所有区别一样,但最重要的区别是我们可以在函数式接口中拥有默认方法,而在抽象类中则不行。这种变化帮助了Java 8中所有集合实现,在使用lambda表达式的foreach()和其他性能方法时产生了改变。

package com.akhi;
public abstract class AbstractDemo {
abstract void letsRun(); // abstract valid
public String toString(); // invalid but valid in interface or functional interface

public boolean equals(Object o); // invalid but valid in interface or functional interface

public int concrete() { // Concrete is invalid in interface
    return 1;
}

public default int getMult(int a, int b) // default invalid but valid in case of functional
{
    return a * b;
}

public static int getSum(int a, int b) // static allowed
{
    return a + b;
}
}


0

抽象类 在以下方面优于 java-8 接口。

  1. 使用抽象类,您可以声明非静态和final字段,并定义公共、受保护和私有的具体方法使用接口,所有字段都自动为public、static和final,并且您声明或定义的所有方法(作为默认方法)都是public的

  2. 可变状态可以与子类共享/修改,而接口只有常量

  3. 抽象类可用于实现Template_method_pattern:它在操作中定义了算法的程序框架,将一些步骤推迟到子类。
  4. 抽象类可用于实现Decorator_pattern:这是一种设计模式,允许在不影响同一类的其他对象的行为的情况下,静态或动态地向单个对象添加行为。

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