Java 8 Lambda表达式
Posted 爱coding的卖油翁
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 8 Lambda表达式相关的知识,希望对你有一定的参考价值。
Java 8 出来很久了,正好在看RXJava,据说学习了lambda和stream api,可以能快速的理解RXJava,于是就来看看Java 8的新特性。
为什么使用 Lambda 表达式?
Lambda表达式是一个匿名函数,是一段可以传递的代码,因此它可以被执行一次或多次。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
以前我们编写一个匿名内部类:
Runnable r = new Runnable()
@Override
public void run()
System.out.println("hello world");
;
用lambda表达式来编写:
Runnable r = () -> System.out.println("hello world");
Lambda 表达式语法
Lambda 表达式在Java 语言中引入了一个新的语法元素和操作符。这个操作符为 “->” , 该操作符被称为 Lambda 操作符或剪头操作符。它将 Lambda 分为两个部分:
- 左侧:指定了 Lambda 表达式需要的所有参数。
- 右侧:指定了 Lambda 体,即 Lambda 表达式要执行的功能。
1.语法格式一:无参,无返回值,Lambda 体只需一条语句。
Runnable r = () -> System.out.println(“hello world”);
2.语法格式二:Lambda 需要一个参数。
Consumer consumer = (arg) -> System.out.println(arg);
3.语法格式三:Lambda 只需要一个参数时,参数的小括号可以省略。
Consumer consumer = arg -> System.out.println(arg);
4.语法格式四:Lambda 需要两个参数,并且有返回值。
BinaryOperator binaryOperator = (x, y) -> return x + y; ;
5.语法格式五:当 Lambda 体只有一条语句时,return 与大括号可以省略。
BinaryOperator bo = (x, y) -> x + y;
类型推断
上述 Lambda 表达式中的参数类型都是由编译器推断得出的。Lambda 表达式中无需指定类型,程序依然可以编译,这是因为 javac 根据程序的上下文,在后台推断出了参数的类型。Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的“类型推断”。
BinaryOperator<String> binaryOperator = (String x, String y) ->
return x + y;
;
这其中的参数类型是可以根据上下文推断出来的,所以可以省略。写成如下代码:
BinaryOperator<String> binaryOperator = (x, y) ->
return x + y;
;
函数式接口
只包含一个抽象方法的接口,你可以通过 Lambda 表达式来创建该接口的对象,被称为函数式接口。若 Lambda表达式抛出一个受检异常,那么该异常需要在目标接口的抽象方法上进行声明。
你可以在任意函数式接口上使用 @FunctionalInterface 注解,这样做有两个好处。首先,编译器会检查标注该注解的实体,检查它是否是只包含一个抽象方法的接口,同时 javadoc 页面也会包含一条声明,说明这个接口是一个函数式接口。
package com.zhou.java8;
@FunctionalInterface
public interface MyFunction
public Integer getValue(Integer num);
作为参数传递 Lambda 表达式:为了将 Lambda 表达式作为参数传递,接收Lambda 表达式的参数类型必须是与该 Lambda 表达式兼容的函数式接口的类型。
@Test
public void test3()
Integer num = operation(100, (x) -> x * x);
System.out.println(num);
private Integer operation(Integer num, MyFunction mf)
return mf.getValue(num);
Java 内置四大核心函数式接口
其他接口
方法引用与构造器引用
当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!(实现抽象方法的参数列表,必须与方法引用方法的参数列表保持一致!)方法引用:使用操作符 “::” 将方法名和对象或类的名字分隔开来。如下三种主要使用情况:
1.对象::实例方法
(x) -> System.out.println(x);
可以写成:
System.out::println;
2.类::静态方法
(x, y) -> Math.pow(x, y);
可以写成:
Math::pow(x, y);
3.类::实例方法
(x, y) -> x.compareToIgnoreCase(y);
可以写成:
String::compareToIgnoreCase;
注意:当需要引用方法的第一个参数是调用对象,并且第二个参数是需要引用方法的第二个参数(或无参数)时,可以用第三种 类::实例方法。
构造器引用:类名::new
与函数式接口相结合,自动与函数式接口中方法兼容。可以把构造器引用赋值给定义的方法,与构造器参数列表要与接口中抽象方法的参数列表一致。
引用无参的构造器:
Supplier<String> supplier = () -> new String();
// 改进的写法
supplier = String::new;
引用有参的构造器:
Function<String, String> function = (n) -> new String(n);
// 改进的写法
function = String::new;
接口中可以有默认的实现方法
package com.zhou.java8;
public interface Color
int getColor();
default String getColorName()
return "red";
默认方法终结了以前的一种经典模式,即提供一个接口,以及实现一个接口的大多数或全部方法的抽象类,如Collection/AbstarctCollection,而现在,你只需要在接口中实现那些方法即可。
如果一个接口中定义了一个默认方法,而另一个父类或接口中又定义了同名的方法,该如何选择呢?
1.选择父类中的方法。如果一个父类提供了具体的实现方法,那么该接口中具体相同名称和参数的默认方法会被忽略。这是”类优先”规则。
2.接口冲突。如果一个父类接口提供了一个默认方法,而另一个接口也提供了一个具体相同名称和参数的方法(不管该方法是否是默认方法),那么你必须通过重写该方法来解决冲突。
package com.zhou.java8;
interface Color
int getColor();
default String getName()
return "red";
interface Pan
void show();
default String getName()
return "pan";
class Car implements Color, Pan
@Override
public void show()
@Override
public int getColor()
return 0;
@Override
public String getName()
return Color.super.getName();
当Car类实现Color和Pan接口时,除了要实现他们的抽象方法,还必须重写它们都拥有的getName()方法。否则,java编译器会抛出一个错误,并让开发人员来解决这个冲突,而不会自动选择的其中一个的。
以上是关于Java 8 Lambda表达式的主要内容,如果未能解决你的问题,请参考以下文章
Kotlin函数 ⑤ ( 匿名函数变量类型推断 | 匿名函数参数类型自动推断 | 匿名函数又称为 Lambda 表达式 )