【问题标题】:What is the purpose of the default keyword in Java?Java中default关键字的作用是什么?
【发布时间】:2015-10-13 05:33:06
【问题描述】:

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(无访问修饰符)有何不同?

【问题讨论】:

  • Java 8 中添加了接口中的默认方法。它不是访问修饰符,而是默认实现。
  • @Eran 你不觉得,引入违反接口定义的默认方法吗? :s
  • 它改变了接口定义。这个定义现在已经过时了。
  • 引入它们是为了支持 lambda。为什么需要它们的详细信息在 Lambda 项目的稻草人提案中。

标签: java interface java-8 default


【解决方案1】:

这是 Java 8 中的一个新特性,它允许 interface 提供一个实现。在 Java 8 JLS-13.5.6. Interface Method Declarations 中描述(部分)

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

What's New in JDK 8 说(部分)

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

【讨论】:

  • 看来,现在接口和抽象类差不多了。 :)
  • @jWeaver 接口仍然不能有构造函数、字段、私有方法或 equals/hashCode/toString 的实现。
  • @Louis Wasserman:在 Java 9 中,他们可以拥有 private 方法。
  • @Dan Pantry:private 方法实际上并不是接口的一部分,但可以用作default 实现或常量初始化器中的辅助方法。请注意,它们已经存在于 Java 8 中,因为当您在接口中使用 lambda 表达式时,会生成合成的 private 方法。因此,Java 9 允许您将该功能用于非合成、非 lambda 用途……
  • @jWeaver 接口和类之间的区别减少到 statebehavior。接口可以携带行为,但只有类可以有状态。 (字段、构造函数、equals/hashCode等方法都是关于状态的。)
【解决方案2】:

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

如果您需要一些令人信服的说法,即 default 是由于 lambdas 而引入的,请注意 Mark Reinhold 在 2009 年的 Lambda 项目的 straw man proposal 提到“扩展方法”作为必须添加的功能支持 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 是默认值,所以如果需要,它可以很容易地被实现类覆盖。

【讨论】:

  • 这不太正确。 Lambdas 可能是直接原因,但它们确实只是压死骆驼的最后一根稻草。真正的动机是启用接口进化(允许现有接口兼容地进化以支持新行为); lambdas 可能是使这种需求脱颖而出的因素,但该功能比这更通用。
  • @BrianGoetz:恕我直言,如果从一开始就存在默认方法,Java 和 .NET 都会受益匪浅。如果可以仅使用接口成员对接口的任何实现执行某些常见操作,但某些实现可能有更有效的方法来执行它们,则接口应该为这些操作定义方法并为它们提供默认实现。无法指定默认实现已经导致接口省略这些方法的压力,并且使得它们无法在以后添加它们。
  • @BrianGoetz 我同意默认方法具有超越 lambda 的重要价值。但我会对您可以提供的任何参考感兴趣,以推动决定将它们包括在内的更广泛的价值。我的阅读是 lambdas 是主要原因(这就是我在回答中使用“主要”这个词的原因)。
  • 也许这份文件会有所帮助:cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html。第 10 节明确指出:“默认方法(以前称为虚拟扩展方法或防御方法)的目的是使接口在初始发布后能够以兼容的方式演进。”然后引用 Lambda 友好的方法作为接口演变的说明
  • @Kartik 你问错问题了!我们不会根据“编译器正确解析程序所需的绝对最小值是多少”来选择语法;我们选择它的依据是“什么能让程序员的意图更直接地向读者展示”。我们首先为用户设计,其次为编译器设计(当涉及到用户时,我们首先为阅读设计,其次为写作设计。)
【解决方案3】:

在其他答案中被忽略的是它在注释中的作用。早在 Java 1.5 中,default 关键字就作为provide a default value 注释字段的一种方式出现。

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

随着 Java 8 的引入,它的使用被重载,以允许在接口中定义默认方法。

还有一点被忽略:声明default class MyClass {} 无效的原因是classes are declared at all 的方式。语言中没有允许该关键字出现在那里的规定。不过,它确实出现在interface method declarations

【讨论】:

    【解决方案4】:

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

    输出是:

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

    【讨论】:

    • 我们不需要覆盖。这是关键点。这有助于我避免使用我的新功能编辑 N 个文件。谢谢你的例子!
    【解决方案5】:

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

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

    【讨论】:

      【解决方案6】:

      新的 Java 8 特性 (Default Methods) 允许接口在使用 default 关键字标记时提供实现。

      例如:

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

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

      向后兼容性: 想象一下,您的接口由数百个类实现,修改该接口将强制所有用户实现新添加的方法,即使它对于实现您的接口的许多其他类不是必需的。

      事实与限制:

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

      2-必须提供正文

      3-它不像接口中使用的其他普通方法那样是公共的或抽象的。

      【讨论】:

        【解决方案7】:

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

        【讨论】:

          【解决方案8】:

          The Java™ Tutorials找到了很好的解释,部分解释如下:

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

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

          【讨论】:

            猜你喜欢
            • 2014-05-26
            • 2010-12-16
            • 1970-01-01
            • 2014-11-29
            • 1970-01-01
            • 1970-01-01
            • 2016-06-03
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多