在 Java 中使用 default 关键字的目的是什么?

Java 中的接口类似于类,但是 接口可以包括 只有抽象的方法final字段 (常数)。

最近,我看到一个问题,看起来像这样

interface AnInterface {
public default void myMethod() {
System.out.println("D");
}
}

根据接口定义,允许使用 只有抽象的方法。为什么它允许我编译上面的代码?default关键字是什么?

另一方面,当我试图写下面的代码,然后它说 modifier default not allowed here

default class MyClass{


}

而不是

class MyClass {


}

谁能告诉我 default关键字的用途?只允许在接口内部使用吗?它与 default(无访问修饰符)有什么不同?

91095 次浏览

这是 Java8中的一个新特性,它允许 interface提供一个实现。在 Java8JLS-13.5.6. 接口方法声明中描述,它读取(部分)

添加一个 default方法,或者将一个方法从 abstract更改为 default,不会破坏与预先存在的二进制文件的兼容性,但是如果预先存在的二进制文件试图调用该方法,可能会导致 IncompatibleClassChangeError。如果限定类型 T是两个接口 IJ的子类型,其中 IJ都声明具有相同签名和结果的 default方法,而且 IJ都不是另一个接口的子接口,则会发生此错误。

JDK 8 中的新特性(部分)说明

默认方法允许将新功能添加到库的接口中,并确保与为这些接口的旧版本编写的代码的二进制兼容性。

默认方法允许您为应用程序的界面添加新功能。它也可以用来拥有一个 多重继承。 除了默认方法之外,还可以在接口中定义静态方法。这使您更容易组织帮助器方法

Java8中引入了一个称为默认方法的新概念。默认方法是那些具有一些默认实现的方法,它们有助于在不破坏现有代码的情况下改进接口。让我们看一个例子:

public interface SimpleInterface {
public void doSomeWork();


//A default method in the interface created using "default" keyword


default public void doSomeOtherWork() {
System.out.println("DoSomeOtherWork implementation in the interface");
}
}


class SimpleInterfaceImpl implements SimpleInterface {


@Override
public void doSomeWork() {
System.out.println("Do Some Work implementation in the class");
}


/*
* Not required to override to provide an implementation
* for doSomeOtherWork.
*/


public static void main(String[] args) {
SimpleInterfaceImpl simpObj = new SimpleInterfaceImpl();
simpObj.doSomeWork();
simpObj.doSomeOtherWork();
}
}

输出是:

   Do Some Work implementation in the class
DoSomeOtherWork implementation in the interface

Java8中添加默认方法主要是为了支持 lambda 表达式。设计人员(在我看来很聪明)决定使用 lambdas 语法来创建接口的匿名实现。但是考虑到 lambdas 只能实现一个方法,它们将被限制在只有一个方法的接口上,这将是一个非常严格的限制。相反,添加了默认方法以允许使用更复杂的接口。

如果你需要一些令人信服的声明,default是由于 Lambda 引入的,请注意,由 Mark Reinhold 在2009年提出的 Lambda 项目的 稻草人求婚,提到“扩展方法”作为一个强制性的特性被添加到支持 lambdas。

下面的例子展示了这个概念:

interface Operator {
int operate(int n);
default int inverse(int n) {
return -operate(n);
}
}


public int applyInverse(int n, Operator operator) {
return operator.inverse(n);
}


applyInverse(3, n -> n * n + 7);

我意识到这是非常人为的,但是应该说明 default是如何支持 lambdas 的。因为 inverse是默认的,所以如果需要的话,它很容易被实现类覆盖。

新的 爪哇8特性(默认方法)允许接口在用 default关键字标记时提供实现。

例如:

interface Test {
default double getAvg(int avg) {
return avg;
}
}
class Tester implements Test{
//compiles just fine
}

Interface Test 使用 default 关键字,该关键字允许接口提供方法的默认实现,而无需在使用该接口的类中实现这些方法。

向下兼容 假设您的接口是由数百个类实现的,修改该接口将强制所有用户实现新添加的方法,尽管对于实现您的接口的许多其他类来说,该方法并不必要。

Facts & Restrictions:

只能在接口中声明,而不能在类或 抽象类。

2-必须提供一个身体

它不像接口中使用的其他常规方法那样被假定为公共或抽象方法。

JavaTM 教程中有一个很好的解释,部分解释如下:

考虑一个涉及计算机控制汽车制造商的例子,这些制造商发布行业标准接口,描述可以调用哪些方法来操作他们的汽车。如果那些计算机控制的汽车制造商在他们的汽车上增加新的功能,比如飞行,会怎样?这些制造商将需要指定新的方法,使其他公司(如电子导航仪制造商)适应他们的软件飞行汽车。这些汽车制造商会在哪里申报这些与飞行相关的新方法?如果他们将这些接口添加到原始接口中,那么实现了这些接口的程序员将不得不重写他们的实现。如果他们将它们作为静态方法添加,那么程序员就会将它们视为实用方法,而不是必不可少的核心方法。

默认方法使您能够向库的接口添加新功能,并确保与为这些接口的旧版本编写的代码的二进制兼容性。

其他答案中忽略了它在注释中的作用。早在 Java 1.5时代,default关键字就是 提供默认值中注释字段的一种方法。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Processor {
String value() default "AMD";
}

它的用法是 超载了和 Java8的引入,允许用户在接口中定义默认方法。

还有一点被忽略了: 声明 default class MyClass {}无效的原因是由于 类都是声明的的方式。语言中没有任何条款允许这个关键字出现在那里。但 是的出现在 接口方法声明上。

接口中的默认方法允许我们在不破坏旧代码的情况下添加新功能。

在 Java8之前,如果向接口添加了一个新方法,那么该接口的所有实现类都必须覆盖该新方法,即使它们没有使用新功能。

使用 Java8,我们可以在方法实现之前使用 default关键字添加新方法的默认实现。

即使使用匿名类或函数接口,如果我们看到一些代码是可重用的,并且我们不想在代码中的任何地方定义相同的逻辑,我们可以编写这些代码的默认实现并重用它们。

例子

public interface YourInterface {
public void doSomeWork();


//A default method in the interface created using "default" keyword
default public void doSomeOtherWork(){


System.out.println("DoSomeOtherWork implementation in the interface");
}
}


class SimpleInterfaceImpl implements YourInterface{


/*
* Not required to override to provide an implementation
* for doSomeOtherWork.
*/
@Override
public void doSomeWork() {
System.out.println("Do Some Work implementation in the class");
}


/*
* Main method
*/
public static void main(String[] args) {
SimpleInterfaceImpl simpObj = new SimpleInterfaceImpl();
simpObj.doSomeWork();
simpObj.doSomeOtherWork();
}
}