Java8 --- 函数式接口@FunctionalInterface及default关键字

最近在开发的过程中有一个点让我比较感兴趣,就是使用Lambda表达式的方式来实现Comparator接口。


1. 关于Comparator和Comparable

既然提到了Comparator,那就大致来说一下Comparator和Comparable接口的区别。
Comparator是一种策略模式,即被比较的对象自身不需要做任何改变(实现任何排序接口),而是通过实例化一个Comparator策略来实现对象之间的排序。
Comparable支持对象自身进行改变(对象自身实现Comparable接口),从而具有排序功能。
举个例子来说,一组对象需要用Collection的sort方法进行排序,有两种方法,要么是对象实现Comparable接口,自身具有可排序属性;要么是实现一个Comparator比较器,在调用sort方法时传入。

下面再来说一说Lambda表达式。

2. Lambda表达式

我们都知道,Lambda表达式是Java里面的函数式编程,那么在使用Lambda表达式首先要满足这样一个条件:首先他的类型是interface,而且有且仅有一个抽象方法。那么下面我们先来看一个使用Lambda表达式的例子。

先看一下Java7中的表达:

public class LambdaTest {
    public static void main(String[] args) {
        Say test1 = new Say() {
            @Override
            public void saySomthing(String something) {
                System.out.println(something);
            }
        };
        test2.saySomthing("hello");
}
interface Say {
    void saySomthing(String something);
}

使用Lambda表达式后的Java8中的表达:

public class LambdaTest {
    public static void main(String[] args) {
        Say test2 = System.out::println;
        test2.saySomthing("hello");
    }
}
interface Say {
    void saySomthing(String something);
}

那么类似的,我们要实现一个Comparator比较器会这样写:

public class LambdaTest {
    public static void main(String[] args) {
       Comparator<Book> bookComparator = Comparator.comparing(Book::getPrice);
       
       Book book1 = new Book("a", 1);
       Book book2 = new Book("b", 1);
       System.out.println(bookComparator.compare(book1, book2));
    }
}
class Book {
    private String name;
    private Integer price;
    
    // 省略constructor/getter/setter
}

这样写完全OK,可是我们去看一下Comparator接口的源码,可能会觉得略微有点诧异。

在这里插入图片描述

可以注意到Comparator接口的源码有这样几个点:

  • @FunctionalInterface注解的作用
  • Comparator接口里有多个方法(两个抽象方法和多个default方法)
  • default关键字的含义

下面逐一来解释一下上面这三个点。

(1) @FunctionalInterface注解的作用

@FunctionalInterface标注在一个接口上,说明这个接口是一个函数式接口。
​那么关于函数式接口,有如下特点:

  • ​有且只有一个抽象方法
  • 可以有多个静态方法
  • 可以有多个default方法(默认方法)
  • 可以有多个Object的public的抽象方法
(2) Comparator接口里有多个方法(两个抽象方法和多个default方法)

可以看到​Comparator接口里除了有compare这个抽象方法,还有一个equals抽象方法,但是如上所说,函数式接口里允许有Object的public的抽象方法。

(3) ​default关键字的含义

default关键字修饰的接口方法可以有默认的方法体。当接口的实现类实现接口的时候,可以不去实现default关键字修饰的方法。
这样做是为了解决这样一种场景:
假设有一个接口定义如下:

interface TestInterFace {
    void doSomething();
}

在一个应用中有该接口的大量实现类,可是突然这个接口里面需要新加一个方法doAnother()。此时,我们要在应用所有实现了这个接口的类中加上doAnother()的实现,这样会导致两个问题:一是修改的幅度对比较大;二是不是所有的实现类都需要去实现doAnother()方法。
default关键字就是为了解决这样的问题。在doAnother()方法前加上default关键字,之前的接口实现类不去实现doAnother()方法也不会报错。如果新的实现类实现这个方法,就等于该实现类覆盖了这个方法,这样最终的运行结果也是符合Java的多态性的。

但是需要注意的是,default关键字也不建议随便使用。
假设有两个接口:InterfaceA和InterfaceB​,他们都有一个default void test()方法,现在又Class C同时实现InterfaceA和InterfaceB,在调用test()方法时就会出错,因为这样会引起二义性,编译器无法知道C调用的究竟是那一个test()方法。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值