# Comparator 为例，Java Lambda 表达式和函数式接口

### Lambda 格式

起因是在多线程编程中看到这种写法:

```java
Thread thread = new Thread(()->{
           System.out.println("test!");
       });
```

其中 Thread() 传入的参数就是一个 Lambda 表达式

可以将 Lambda 表达式看作一个方法的简写，Lambda 表达式由三部分组成，() 中的是方法中的传入参数，{} 是方法体，-&gt; 表示将参数传入方法体中。

```plaintext
()->{}
```

比如：

```java
(String first,String second) -> {
            return  first.length()-second.length();
        }
```

当参数的类型可以推断，比如 first.length() 需要一个 String 类型，那 first 的类型就可以被忽略。

```java
(first,second) -> {
            return  first.length()-second.length();
        }
```

当只有一个参数的时候，() 甚至可以被省略：

```java
event -> {
            System.out.println(event);
        }
```

### 为什么引入 Lambda 表达式

在 Java 中，一切皆对象，如果不采用 Lambda 表达式的话，我们需要先 new 一个对象出来，然后实现这个对象中的方法，再将这个对象作为参数传入，我们以 Comparator 接口作为例子：

我们要将 words 数组中的字符串按照从小到大排序，Arrays.sort 方法需要一个传入一个 Comparator 类型的对象，不使用 Lambda 表达式时：

```java
String[] words =new String[]{"abc","bc","c"};

Comparator<String> comparator = new Comparator<String>() {
    @Override
    public int compare(String first, String second) {
        return first.length() -second.length();
    }
};
Arrays.sort(words,comparator);

for (int i = -1; i < words.length; i++) {
    System.out.println(words[i]);
}
```

> 注意，接口是不能被实现的，即接口是不能被 new 的，被 new 的是实现接口的实现类。

使用 Lambda 表达式之后：

```java
String[] words =new String[]{"abc","bc","c"};

Arrays.sort(words,(String first,String second) -> {
    return  first.length()-second.length();
});

for (int i = -1; i < words.length; i++) {
    System.out.println(words[i]);
}
```

所以 Lambda 表达式存在的主要意义就是忽略了 new 操作，节省了不少代码。

那么什么时候可以用 Lambda 表达式来表示对象呢？

当接口是函数式接口的时候可以用 Lambda 表达式来代替实现类。

### 函数式接口

对于**有且只有一个抽象方法**的接口，需要这种接口的对象时，就可以提供一个 lambda 表达式。这种接口称为函数式接口（functional interface）。

比如并发编程中经常见到的 Runnale 接口：

```java
public interface Runnable {
	abstract void run();
}
```

> 在 Java 接口，所有的**抽象方法**都是 public 和 abstract 的，所以这两个关键字可以省略

Java 接口中的方法不都是抽象的吗？难道还有不是抽象的方法？

我们以 Comparator 接口为例：

```java
public interface Comparator<T> {

    int compare(T o1, T o2);
    
    boolean equals(Object obj);
    
    default Comparator<T> reversed() {
        return Collections.reverseOrder(this);
    }
    
   	public static <T extends Comparable<? super T>> Comparator<T> reverseOrder() {
        return Collections.reverseOrder();
    }
    ......
```

在上面的示例中，我们可以用 Lambda 表达式来代替 Comparator 类型的对象，Comparator 也是一个函数式接口，不是说有且只能有一个抽象方法吗？

Comparator 确实只有一个抽象方法 compare(T o1, T o2) 。**equals 方法是 Comparator 接口重声明 Object 类中的 equals 方法，并不是一个抽象方法。**

在下面的示例中，class B 并没有被强制重写 equals 方法，所以 equals 并不是一个抽象方法。

```java
interface A{
    boolean equals(Object obj);
}
class B implements A{

}
class C{
    public static void main(String[] args) {
        A a =new B();
        System.out.println(a.equals(a));
    }
}
// 打印结果为 true
```

在接口中重写 Object 类方法主要是出于文档的目的，为了细化 equals 方法的定义。Java API中的一些接口会重新声明Object方法来附加javadoc注释。Comparator API就是这样一个例子。

在 Java1.8 之后，接口中的方法可以有方法体，比如 Comparator 接口中带有 default 和 static 关键字的方法，default 是方法的默认实现，static 是接口中的静态方法，有了 static 方法就可以直接使用而不用再new 一个对象出来了，虽然不违背 Java 语法，但不符合接口的设计初衷。

### 接口相关

* 接口绝不能被实例化，即绝不能 new 一个接口出来。
    
* 接口绝不能包含变量。
    
* 在接口中可以定义常量，常量被自动设为 public static final 类型。
    
* 在 Java8 之后，在接口中可以实现方法（default,static 方法）。
    
* 接口中的所有方法自动地属于 public。因此，在接口中声明方法时，不必提供关键字 public。也不需要abstract 关键字
    
* 在实现接口时，必须把方法声明为public；否则，编译器将认为这个方法的访问属性是包可见性，即类的默认访问属性，之后编译器就会给出试图提供更严格的访问权限的警告信息。
