【发布时间】:2020-05-14 00:09:57
【问题描述】:
我在一些 StackOverflow 的答案中看到了术语抽象方法、具体方法和默认方法的“不同”定义。
Java 语言规范给出的真正定义是什么?请在您的回答中包含相关的支持 JLS 参考资料。
【问题讨论】:
-
注意:我不是要求对这些术语进行解释。这是一个“语言律师”问题。
我在一些 StackOverflow 的答案中看到了术语抽象方法、具体方法和默认方法的“不同”定义。
Java 语言规范给出的真正定义是什么?请在您的回答中包含相关的支持 JLS 参考资料。
【问题讨论】:
下面的链接/章节编号取自 Java 11 版本的 Java 语言规范。
根据JLS 8.4.3.1:
“
abstract方法声明将方法作为成员引入,提供其签名(第 8.4.2 节)、结果(第 8.4.5 节)和 throws 子句(如果有)(第 8.4.6 节),但没有提供一个实现(第 8.4.7 节)。不是abstract的方法可以称为具体方法。"
根据JLS 9.4:
“默认方法是用
default修饰符在接口中声明的实例方法。它的主体总是由一个块表示,它为任何实现接口而不重写方法的类提供默认实现。默认方法不同于在类中声明的具体方法(第 8.4.3.1 节),也不同于既不被继承也不被覆盖的私有接口方法。”
所以根据这个分类法,实际上有 4 种方法:
请注意,JLS 8.4.3.1 在抽象方法和具体方法之间的区别中没有提及 final 或 static。
这些修饰符不能与abstract 关键字一起使用。这意味着static 或final 的方法必须是具体方法。这加强了具体方法的 8.4.3.1 定义。
【讨论】:
Java 语言规范 (JLS) Section 8.4.3.1 中将抽象方法定义为:
抽象方法声明将方法作为成员引入,提供其签名(第 8.4.2 节)、结果(第 8.4.5 节)和 throws 子句(如果有)(第 8.4.6 节),但不提供实现(第 8.4.7 节)。
在实践中,抽象方法是定义了签名但没有提供实现的任何方法。例如,接口中的方法和抽象类中使用abstract关键字限定的方法都是抽象方法:
public interface Foo {
void someAbstractMethod();
}
public abstract class Bar {
public abstract someAbstractMethod();
// ...
}
根据JLSSection 8.4.3.1,具体方法定义为:
非抽象的方法可以称为具体方法。
实际上,这意味着提供了实现的任何方法:
public FooImpl implements Foo {
@Override
public void someAbstractMethod() {
// ... some implementation ...
}
}
默认方法定义在 JLS Section 9.4:
默认方法是在带有默认修饰符的接口中声明的实例方法。它的主体总是由一个块表示,它为任何实现接口而不覆盖方法的类提供默认实现。默认方法不同于在类中声明的具体方法(第 8.4.3.1 节),也不同于既不继承也不覆盖私有接口方法。
同样的部分还添加了:
接口可以声明
static方法,这些方法无需引用特定对象即可调用。static接口方法不同于默认方法,默认方法是实例方法。
默认方法是为特定目的而创建的。在 JDK 8 中,函数式接口被添加到 Java 中。这需要更新接口以包含功能方法,但这样做需要所有这些接口的现有实现(包括在第 3 方库和框架中)提供实现。相反,Java 团队引入了默认方法,它们是提供默认实现的接口方法,当覆盖类没有提供实现时使用该实现。
这不应该用作抽象类的替代品。它是为特定目的而设计的,应该用于该目的。
实际上,默认方法是使用default 关键字创建的:
public interface Foo {
public default void someMethod() {
// ... some default implementation ...
}
}
这个默认实现可以在具体的子类中被覆盖:
public class FooImpl implements Foo {
@Override
public void someMethod() {
// ... some overriding implementation ...
}
}
此外,根据 JLS Section 9.4.1.1,方法的默认实现(默认方法的主体)可以使用super 关键字在具体子类中访问,由接口名称限定:
可以使用方法调用表达式(第 15.12 节)访问被覆盖的默认方法,该表达式包含由超级接口名称限定的关键字
super。
例如:
public class FooImpl implements Foo {
@Override
public void someMethod() {
Foo.super.someMethod();
}
}
接口名称用作限定符是因为一个类可以实现多个接口(或者一个接口可以扩展多个接口)。如需更多信息,请参阅Explicitly calling a default method in Java。
【讨论】: