博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java8: Functional Interface and Lambda Expression
阅读量:5816 次
发布时间:2019-06-18

本文共 4452 字,大约阅读时间需要 14 分钟。

写在前面: 最近在看<<Java8实战>>,感觉这本书的排版,纸质,内容真的都超级棒,个人觉得这本书还是很值得一读.本文简单或详细介绍一下Java8的FunctionalInterface和Lambda表达式.

What is Functional Interface?

函数式接口(Functional Interface)是在Java8才开始引入的.首先看一下Java普通接口和函数式接口的区别:

  • Java普通接口: 指的是一些具有相同属性和行为的类的抽象.
  • 函数式接口: 也是同样的理解方式,它是对一些相似的方法的抽象.

How can we say those methods are similar methods?

1.如果定义的是一个泛型的函数式接口的话,比如:

@FunctionalInterfacepublic interface MyFunction1
{ R calculate(T t);}

那么所有只有一个参数并有返回值的函数都是MyFunction1的一个实例.也就是说这些方法都是相似的方法.

2.如果定义的是一个具体的函数式接口,比如:

@FunctionalInterfacepublic interface MyFunction2 {    void print(Integer i);}

那么所有参数是Integer类型的并且没有返回值的函数都是MyFunction2的一个实例.也就是说这些方法都是相似的方法.

Java的接口可以当做函数的参数,那么函数式接口自然也可以,这就叫做行为参数化.

How to define and use a funtional interface?

在Java8中已经定义了好多的函数式接口,比如:

  • java.lang.Runnable
  • java.util.Comparator
  • java.util.function.Predicate
  • java.util.function.Consumer

那么定义一个函数式接口,有哪些需要注意的地方呢?

  • 1.添加@FunctionalInterface注解.
  • 2.有且只有一个抽象方法.可以有多个default方法和static方法,但是这些方法都必须实现.

对于@FunctionalInterface注解,其实不加也可以,完全是可以通过编译的,但前提是这个接口需要满足上面的第二个注意点.

但又是为什么需要@FunctionalInterface?首先我们先看一个例子:

假设我们定义了一个函数式接口,但是忘记了添加了@FunctionInterface,同时这个函数式接口还没被其他地方引用,如果这个时候我们又添加了一个抽象方法,是不会报错的.因为这个接口有了多个抽象方法,所以这个接口就是一个普通的接口,而不是我们所期望的函数式接口.

这时候,如果添加了@FunctionalInterface注解的话,就会报错,提示说这个接口具有多个抽象方法.实际上,个人理解@FunctionalInterface就是手动添加约束,说明这个接口就是函数式接口,只能有一个抽象方法.

接下来编写并使用一个自定义函数式接口

// define a functional interface@FunctionalInterfacepublic interface MyFunction
{ R convert(T t);}// use a function interfacepublic class Test { public static void main(String[] args) { MyFunction
f = (Long l) -> String.valueOf(l); System.out.println(f.convert(10L)); // f: convert a Long to String MyFunction
g = (String s) -> Long.parseLong(s); System.out.println(g.convert("10")); // g: convert a String to Long }}

What is Lambda expression?

咱们可以看一看<<Java8实战>>这本书中的定义:

可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式: 它没有名称,但它有参数列表,函数主体,返回类型,可能还有一个可以抛出的异常列表.

  • 匿名: 是因为它不像普通的方法有一个明确的名称
  • 函数: 是因为Lambda函数不像方法那样属于某个特定的类,但又和方法一样,有参数列表,函数主体,返回值类型,还可能有可以抛出的异常列表
  • 传递: Lambda表达式可以作为参数传递给方法或存储在变量中
  • 简洁: 无须像匿名类那样写很多模板代码

个人理解,Lambda表达式完全是服务于函数式接口的,就是为了在创建一个函数式接口实例时更加的直观,简洁.比如咱们要创建一个匿名类对象时,可以看一下普通接口和函数式接口的区别:

// a common interfacepublic interface Common {    void f();    void g();}@FunctionalInterfacepublic interface MyFunction
{ R convert(T);}public class Test { public static void main(String[] args){ // create a instance of common interface Common c = new Common() { @Override public void f() {} @Override public void g() {} }; // create a instance of functional interface MyFunction
f = (Long l) -> String.valueOf(l); }}

Method Reference

说起方法引用之前,咱们先看一个例子

// 1    MyFunction
f = (Long l) -> String.valueOf(l); // 2: 通过Lambda的上下文推断出l的数据类型 MyFunction
f = l -> String.valueOf(l); // // 3 MyFunction
f = String::valueOf;

第一种,第二种和第三种写法的作用完全是一样的,但显然第三种的写法更加简洁.形如xxx::xxx这样的表达式就是方法引用.方法引用可以被看做仅仅调用特定方法的Lambda的一种快捷写法.

How many ways to replace lambda expression with method reference?

总共有四种方法引用,下面咱们根据一个实例来说明并解释这几种方法引用.

// common classpublic class FunctionInstance {    public Integer getLength(String str) {        return Optional.ofNullable(str).orElse("").length();  // Optional也是Java8引入的    }}// test classpublic class MethodReference {    public static void main(String[] args) {        // 1.指向静态方法的方法引用        //Function
f1 = s -> Integer.parseInt(s); Function
f1 = Integer::parseInt; // 2.指向任意类型实例方法的方法引用,s为内部对象 //Predicate
f2 = s -> s.isEmpty(); Predicate
f2 = String::isEmpty; // 3.指向现有对象(外部对象)的实例方法的方法引用, instance为外部对象 //Function
f3 = s -> instance.getLength(s); FunctionInstance instance = new FunctionInstance(); Function
f3 = instance::getLength; // 4.构造函数引用 //Supplier
f4 = () -> new MethodReference(); Supplier
f4 = MethodReference::new; }}

2和3有人可能会混淆, 反正一开始看的时候我是没区分出来.其实就看参数是为内部对象还是外部对象.内部对象就是用2,外部对象就是用3

  • 内部对象: 指Lambda表达式中的对象,比如上面的s, 传入的参数,属于内部对象
  • 外部对象: 指的是Lambda表达式外的对象,比如上面的instance

Summary

函数式接口: 就是一个特殊的接口,只能有一个抽象方法.所以可以将函数式接口想象成是一个函数类型(比如Predicate,Consumer或自定义的函数式接口).

Lambda表达式: 则是简化了匿名类对象的创建.正如我在上文所说的,Lambda表达式完全是服务于函数式接口的,正是因为了函数式接口的特殊性,所以才使用了Lambda表达式来创建一个函数类型实例,而不是像匿名类那样写一堆无用的模板代码.

转载地址:http://zdqbx.baihongyu.com/

你可能感兴趣的文章
盘点物联网网关现有联网技术及应用场景
查看>>
mui 总结2--新建第一个app项目
查看>>
nginx的lua api
查看>>
考研太苦逼没坚持下来!看苑老师视频有点上头
查看>>
HCNA——RIP的路由汇总
查看>>
zabbix监控php状态(四)
查看>>
定时任务的创建
查看>>
实战Django:小型CMS Part2
查看>>
原创]windows server 2012 AD架构试验系列 – 16更改DC计算机名
查看>>
统治世界的十大算法
查看>>
linux svn安装和配置
查看>>
SSH中调用另一action的方法(chain,redirect)
查看>>
数据库基础
查看>>
表格排序
查看>>
关于Android四大组件的学习总结
查看>>
java只能的round,ceil,floor方法的使用
查看>>
由于无法创建应用程序域,因此未能执行请求。错误: 0x80070002 系统找不到指定的文件...
查看>>
新开的博客,为自己祝贺一下
查看>>
puppet任务计划
查看>>
【CQOI2011】放棋子
查看>>