JAVA系列---函数式接口

函数式接口的定义

  1. 一个函数式接口有且仅有一个抽象方法(SAM,single abstract method)。对于接口来说抽象方法必须重写,默认方法可选重新,静态方法不可重新。
  2. Object 类中的 public abstract method 不会被视为单一的抽象方法。这些方法对于函数式接口来说,不被当成是抽象方法(虽然它们是抽象方法)。因为任何一个函数式接口的实现,默认都继承了Object类,包含了来自java.lang.Object里对这些抽象方法的实现。
  3. 函数式接口可以用@FunctionalInterface 注解进行修饰。但并不是必须用注解标注,一个接口只要满足只有一个抽象方法的条件就可以称为函数式接口。
    如果使用了此注解,再往接口中新增抽象方法,编译器就会报错,编译不通过。换句话说,@FunctionalInterface就是一个承诺,承诺该接口世世代代都只会存在这一个抽象方法。因此,凡是使用了这个注解的接口,开发者可放心大胆的使用lambda来实例化。当然误用@FunctionalInterface带来的后果也是极其惨重的:如果哪天你把这个注解去掉,再加一个抽象方法,则所有使用lambda实例化该接口的客户端代码将全部编译错误。所以当某接口只有一个抽象方法,但没有增加@FunctionalInterface,则代表该接口不承诺未来不增加抽象方法,所以建议不要用lambda来实例化,还是老老实实的用以前的方式比较稳妥。

函数式接口的示例

@FunctionalInterface
public interface HelloWorldService {
   
   
	
    void sayHello(String msg);
    
    default void doSomeWork1() {
   
   
        // Method body
    }

    default void doSomeWork2() {
   
   
        // Method body
    }
    static void printHello() {
   
   
        System.out.println("Hello");
    }
    
	@Override
    boolean equals(Object obj);

}

函数式接口的用处

函数式接口带给我们最大的好处就是:可以使用极简的lambda表达式实例化接口。为什么这么说呢?我们或多或少使用过一些只有一个抽象方法的接口,比如 Runnable, ActionListener, Comparator …。比如,我们要用Comparator实现排序算法,我们的处理方式通常无外乎两种:

  • 规规矩矩的写一个实现Comparator接口的java类去封装排序逻辑。若业务需要多种排序方式,那就得写多个类提供多种实现,而这些实现往往只需使用一次
  • 另外一种聪明些的做法无外乎就是在需要的地方搞个匿名内部类。比如:
class Test {
   
    
    public static void main(String args[]) {
   
    
        List<Person> persons = new ArrayList<Person>();
        Collections.sort(persons, new Comparator<Person>(){
   
   
            @Override
            public int compare(Person o1, Person o2) {
   
   
                return Integer.compare(o1.getAge(), o2.getAge());
            }
        });
    } 
} 

匿名内部类实现的代码量没有多到哪里去,结构也还算清晰。不过如今脚本语言横行无阻,无不以其简洁的语法为杀手锏,俘获程序员的欢心。jdk开发者们应该是意识到了这一点,我们从上面Comparator的实现就可以得到验证。该接口在jdk8的实现增加了FunctionalInterface注解,代表Comparator是一个函数式接口,使用者可放心的通过lambda表达式来实例化。那我们来看看使用lambda实例化所需编写的代码:

Comparator<Person> comparator = (p1, p2) -> Integer.compare(p1.getAge(), p2.getAge());

lambda表达式

lambda这个名称是由美国数学家 Alonzo Church选择的,他在 1930 年代发展计算理论时需要一个希腊字母来表示函数抽象的运算符(λ)。所以其实没什么特殊意义,对于名称纠结症的可以放一放 了。

lambda表达式其实是快速创建函数式接口实例的语法糖; -> 就是lambda的语法标志。

lambda是什么

lambda表达式是实现函数式接口的一个快捷方式。下面这段代码是最直观的体现:

Runnable task = () -> System.out.println("i am coming...");

=号的前面是变量的声明,后面是一个lambda表达式。我们直接将lambda表达式赋值为Runnable类型的task变量,就意味着:lambda表达式是实现函数式接口的一个快捷方式。理解这一点是至关重要的。

lambda表达式语法

lambda表达式语法可抽象表示为:

(Type1 param1, Type2 param2, ..., TypeN paramN) -> {
   
   
     statment1;
     statment2;
     ...
     return statmentN;
}

以Predicate判断年龄为例

Predicate<Person> predicate = (Person person) -> {
   
   
    Integer age = person.getAge();
    return age >= 18;
};

但上面的并不是我们常见的代码,那是因为我们常用的是极简风,以下是简化过程:参数类型省略-》参数占位省略-》返回语句省略。

  1. 参数类型省略:因为编译器可以从上下文环境中推断出lambda表达式的参数类型

    (param1, param2, ..., paramN) -> {
         
         
        statment1;
        statment2;
        ...
        return statmentN;
    }
    
    Predicate<Person> predicate = (person) -> {
         
         
        
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lipviolet

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值