💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
# Java 默认方法教程 > 原文: [https://howtodoinjava.com/java8/default-methods-in-java-8/](https://howtodoinjava.com/java8/default-methods-in-java-8/) 在上一篇文章中,我们了解了 [**Lambda 表达式和函数式接口**](//howtodoinjava.com/java8/complete-lambda-expressions-tutorial-in-java/ "Complete lambda expressions tutorial") 。 现在,让我们继续进行讨论,并讨论另一个相关特性,即**默认方法**。 好吧,这对于 Java 开发人员来说确实是革命性的。 到 Java 7 为止,我们已经学到了很多关于接口的知识,而在编写代码或设计应用程序时,所有这些事情都已经牢记在心。 在引入默认方法之后,其中一些概念将从 Java 8 发生巨大变化。 ```java I will discuss following points in this post: What are default methods in java 8? Why default methods were needed in java 8? How conflicts are resolved while calling default methods? ``` ## Java 8 中的默认方法是什么? 默认方法使您可以向库的接口添加新功能,并确保与为这些接口的较早版本编写的代码二进制兼容。 顾名思义,java 8 中的默认方法就是默认的。 如果不覆盖它们,则它们是调用者类将调用的方法。 它们在接口中定义。 让我们看一个例子: ```java public interface Moveable { default void move(){ System.out.println("I am moving"); } } ``` 可移动接口定义方法`move()`; 并提供了默认实现。 如果有任何类实现此接口,则无需实现其自己的`move()`方法版本。 它可以直接调用`instance.move()`; ```java public class Animal implements Moveable{ public static void main(String[] args){ Animal tiger = new Animal(); tiger.move(); } } Output: I am moving ``` 而且,如果类愿意定制行为,那么它可以提供自己的自定义实现并覆盖该方法。 现在将调用它自己的自定义方法。 ```java public class Animal implements Moveable{ public void move(){ System.out.println("I am running"); } public static void main(String[] args){ Animal tiger = new Animal(); tiger.move(); } } Output: I am running ``` 这还没有全部完成。 最好的部分是以下好处: 1. 静态默认方法:您可以在接口中定义静态默认方法,这些方法将对实现该接口的所有类实例可用。 这使您可以更轻松地在库中组织帮助程序方法。 您可以将特定于接口的静态方法保留在同一接口中,而不是在单独的类中。 这使您可以在类之外定义方法,但仍可与所有子类共享。 2. 它们为您提供了一种非常理想的特性,可以在不触及其代码的情况下为多个类添加功能。 只需在它们都实现的接口中添加默认方法即可。 ## 为什么 Java 8 需要默认方法? 这是您下一个[**面试问题**](//howtodoinjava.com/java-interview-questions/ "Java Interview Questions")的不错的选择。 **最简单的答案是在 Java 中启用 lambda 表达式的特性。** Lambda 表达本质上是函数式接口的类型。 为了无缝支持 lambda 表达式,必须修改所有核心类。 但是,这些核心类(例如`java.util.List`)不仅在 JDK 类中实现,而且在数千个客户端代码中也实现。 核心类中任何不兼容的更改都将肯定会产生反作用,并且根本不会被接受。 默认方法打破了这种僵局,并允许在核心类中添加对函数式接口的支持。 让我们来看一个例子。 以下是已添加到`java.lang.Iterable`的方法。 ```java default void forEach(Consumer<? super T> action) { Objects.requireNonNull(action); for (T t : this) { action.accept(t); } } ``` 在 Java 8 之前,如果必须迭代 Java 集合,则将获得一个迭代器实例,并调用它的下一个方法,直到`hasNext()`返回`false`。 这是常见的代码,我们在日常编程中已经使用了数千次。 语法也总是相同的。 因此,我们可以使其精简吗,使其仅占用一行代码,仍然像以前一样为我们完成工作。 上面的函数可以做到这一点。 现在要迭代并对列表中的每个项目执行一些简单的操作,您需要做的是: ```java import java.util.ArrayList; import java.util.List; public class Animal implements Moveable{ public static void main(String[] args){ List<Animal> list = new ArrayList(); list.add(new Animal()); list.add(new Animal()); list.add(new Animal()); //Iterator code reduced to one line list.forEach((Moveable p) -> p.move()); } } ``` 因此,这里,在不破坏列表的任何自定义实现的情况下,已将其他方法添加到`List`中。 长期以来,它一直是 Java 中非常需要的特性。 现在就在我们身边。 ## 调用默认方法时如何解决冲突? 到目前为止,一切都很好。 我们的所有基础知识都很好。 现在转到复杂的事情。 在 Java 中,一个类可以实现 N 个接口。 另外,一个接口也可以扩展另一个接口。 如果在两个这样的接口中声明了默认方法,则该接口由单个类实现。 那么很明显,类会混淆调用哪个方法。 **解决此冲突的规则如下:** 1)最优选的是类中的覆盖方法。 如果在匹配任何内容之前找到它们,它们将被匹配并被调用。 2)选择在“最特定的默认提供接口”中具有相同签名的方法。 这意味着如果`Animal`类实现了两个接口,即`Moveable`和`Walkable`,从而`Walkable`扩展了`Moveable`。 那么`Walkable`是最具体的接口,如果方法签名匹配,则将从此处选择默认方法。 3)如果`Moveable`和`Walkable`是独立的接口,则会发生严重的冲突情况,并且编译器将抱怨并无法确定。 您必须通过提供额外的信息来帮助编译器,该信息应从哪个接口调用默认方法。 例如: ```java Walkable.super.move(); //or Moveable.super.move(); ``` 这就是这里的全部内容。 下次,当我想到一些有趣的事情时,我将继续讨论。 不要忘记发表您的评论/想法或问题。 **祝您学习愉快!**