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

```

这就是这里的全部内容。 下次，当我想到一些有趣的事情时，我将继续讨论。 不要忘记发表您的评论/想法或问题。

**祝您学习愉快！**