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


93

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(无访问修饰符)有什么区别?


4
接口8中的默认方法是在Java 8中添加的。它不是访问修饰符,而是默认实现。
伊兰2015年

2
@Eran你不认为,默认方法的引入违反了接口定义吗?:s
拉维

2
它更改了接口定义。该定义现在已过时。
Louis Wasserman

2
引入它们是为了支持lambda。为什么需要它们的详细信息,请参见Lambda项目的建议。
短跑运动员

Answers:


72

这是Java 8中的一项新功能,可interface提供一个实现。在Java 8 JLS-13.5.6中描述读取(部分)接口方法声明

添加default方法,或改变的方法,由abstractdefault,不破与预先存在的二进制兼容性,但可能会导致IncompatibleClassChangeError如果一个预先存在的二元试图调用的方法。如果限定类型,T是两个接口的子类型IJ,则会出现此错误,其中I和都J声明一个default具有相同签名和结果的方法,而另一个I也不J是另一个子接口。

JDK 8的新增功能说(部分)

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


16
看来,现在接口和抽象类几乎相同。:)
拉维

16
@jWeaver接口仍然不能具有构造函数,字段,私有方法或equals / hashCode / toString的实现。
Louis Wasserman

10
@Louis Wasserman:在Java 9中,他们可以有private方法。
Holger 2015年

6
@Dan Pantry:private方法实际上并不是接口的一部分,但可以用作default实现的帮助器方法或在常量初始化程序中使用。请注意,它们已经存在于Java 8中,因为当您在接口中使用lambda表达式时,private会生成综合方法。因此,Java 9允许您将该功能也用于非合成,非lambda用途…
Holger 2015年

14
@jWeaver接口和类之间的差异减少到状态vs行为。接口可以带有行为,但是只有类可以具有状态。(字段,构造函数和方法,例如equals / hashCode都是关于状态的。)
Brian Goetz 2015年

26

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

如果您需要令人信服的defaultlambda声明,请注意,马克·赖因霍尔德(Mark Reinhold)在2009年提出的Lambda项目的稻草人建议中提到,“扩展方法”是为支持lambda而添加的强制性功能。

这是一个演示概念的示例:

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支持lambda。因为inverse是默认设置,所以可以根据需要轻松地由实现类覆盖。


8
这是不正确的。Lambdas可能是最直接的原因,但实际上它们只是打破骆驼背的稻草。真正的动机是使接口能够发展(允许现有接口兼容地发展以支持新行为);lambda可能是导致这种需求突显的因素,但是功能比这更普遍。
Brian Goetz

@BrianGoetz:恕我直言,如果从一开始就存在默认方法,那么Java和.NET都将受益匪浅。如果仅使用接口成员就可以对接口的任何实现执行某些通用操作,但是某些实现可能有更有效的方式来执行该操作,则该接口应为这些操作定义方法并为其提供默认实现。无法指定默认实现会导致要求接口忽略此类方法的压力,并使它们以后无法添加它们。
超级猫

@BrianGoetz我同意默认方法具有超越lambda的重要价值。但我对您可以参考的更广泛的价值感兴趣,因此可以决定将它们包括在内。我的理解是,lambda是主要原因(这就是为什么我在回答中使用“主要”一词的原因)。
短跑运动员

2
也许该文档会有所帮助:cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html。第10节明确指出:“默认方法(以前称为虚拟扩展方法或防御者方法)的目的是使接口在首次发布后能够以兼容的方式演进。” 然后引用了Lambda友好方法作为界面演化的例证
Brian Goetz

2
@Kartik您在问错问题!我们不会基于“编译器正确解析程序所需的绝对最小值是多少”来选择语法;我们基于“将使读者的程序员的意图更直接地体现给读者的理由”来选择它。我们首先为用户设计,然后为编译器设计(就用户而言,我们首先为阅读而设计,其次是为写作。)
Brian Goetz

16

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

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

输出为:


在接口的DoSomeOtherWork类中执行Some Work实现


16

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

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

Java 8的引入使该用法的使用量过多,以允许在接口中定义默认方法。

其他被忽略的东西:声明default class MyClass {}无效的原因是由于类的声明方式。语言中没有规定允许该关键字出现在该位置。不过,它确实出现在接口方法声明中


3

新的Java 8功能(默认方法)允许接口在用default关键字标记时提供实现。

例如:

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

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

向后兼容性: 假设您的接口由数百个类实现,修改该接口将迫使所有用户实现新添加的方法,尽管对于实现您接口的许多其他类而言,该方法不是必需的。

事实与限制:

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

2-必须提供身体

3-不假定它是公共的或抽象的,而不是接口中使用的其他常规方法。


3

界面中的默认方法使我们能够添加新功能而不会破坏旧代码。

在Java 8之前,如果将新方法添加到接口,则该接口的所有实现类都必须重写该新方法,即使它们没有使用新功能。

对于Java 8,我们可以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();
      }
   }

2

Java™教程中可以找到很好的解释。,部分解释如下:

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

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


1

默认方法使您可以向应用程序界面添加新功能。它也可以用于具有多重继承。除了默认方法外,您还可以在接口中定义静态方法。这使您更轻松地组织帮助程序方法

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.