java 函数fun_Java8新特性(二)-Lambda表达式和函数式接口

本文介绍了Java8引入的Lambda表达式及其使用方法,包括无参无返回值、一个参数无返回值、多参有返回值的Lambda形式,并通过实例展示了如何简化匿名内部类。同时,文章解释了函数式接口的概念,强调了它们在Java8中的重要性,以及如何使用函数式接口如Consumer、Supplier、Function和Predicate。此外,还讨论了方法引用和构造器引用的应用。
摘要由CSDN通过智能技术生成

1. Lambda

java8开始引入了Lambda表达式.lambda表达式是Stream API编程的基础。本篇文章不打算研究java中Lambda的底层实现。主要关注以下两点。

主要关注什么是Lambda

在Java中如何使用Lambda

如何使用函数式接口

1.1 什么是Lambda表达式

广义上

广义上讲Lambda表达式是一种匿名函数.即无序标识符的函数, 只拥有形参,和方法体.

Java中的Lambda

Java中的Lambda表达式是可以认为是一种匿名内部类的语法糖.

1.2 如何使用Lambda表达式

前言

Lambda表达式最主要的一个应用场景就是用来简化匿名内部类的写法, 使得代码更加的简短, 简洁.但是如果不了解Lambda表达式, 可能对于其他开发人员来说你写的代码可能就不是那么好维护.

基本语法

基本语法:

(parameters) ->{statements;}

分类

我们按照匿名内部类来对Lambda表达式进行分类。可以分为以下几类。

一. 无参,无返回值

Runnable r1 = ()->{System.out.println("Hello Lambda");};

new Thread(r1).start()

可以写得更简洁new Thread( ()->{System.out.println("Hello Lambda");}).start()

二. 一个参数, 无返回值

Consumercon = (String str)->{System.out.println(str);};

这里的Consumer是函数式接口, 表示的是一个消费型函数式接口。如下是Consumer的定义. 这边的lambda表达式, 相当于new了一个匿名内部类对象,并实现了其accept方法。lambda表达式就是不用写要实现的方法名, 应为JVM会给我们自动推断, 该lambda表达式是要绑定的对应方法.

@FunctionalInterface

public interface Consumer {

void accept(T t);

default Consumer andThen(Consumer super T> after) {

Objects.requireNonNull(after);

return (T t) -> { accept(t); after.accept(t); };

}

}

三. 需要两个参数或两个以上, 且有返回值

例如用lambda替换创建匿名内部类Comparator

Comparator comparator = new Comparator() { // 匿名内部类方式

@Override

public int compare(Integer o1, Integer o2) {

return o1.compareTo(o2);

}

};

// lambda方式

Comparator comparator2 = (Integer o1, Integer o2)->{ o1.compareTo(o2)};

四. 简略写法

上述写法还就可以简略, 遵守以下规则。

简化规则总结:

参数的小括号可以省略, 当且仅当只有一个参数.

参数的数据类型可以省略, 当且仅当lambda表达式.

方法体的return和大括号可以省略, 当且仅当只有一条语句.

以Comparator接口为例子

Comparator comparator2 = (o1, o2)-> o1.compareTo(o2);

类型推断与lambda参数

如上述总结的规则,lambda表达式参数列表的类型可以省略。是因为编译器在后面给我们做了很多事, 编译器可以根据上下文推断出lambda表达式的参数类型

1.3 函数式接口

函数式接口(Funcation Interface)是一种特殊的接口。这类接口只包含一个抽象方法的接口, 因此也被称为SAM(Single Abstract Method).

JDK 8中又增加了java.util.function包, 提供了常用的函数式接口。

为什么会单单从接口中定义出此类接口呢?

定义

只包含一个抽象方法的接口, 称为函数式接口(除了隐含的Object对象的公共方法)

函数式接口中可以额外定义多个抽象方法,但这些抽象方法签名必须和Object的public方法一样(必须满足即使Object的方法, 又是public的接口, 像Object的clone()接口就不是public)

@FunctionalInterface

public interface ObjectMethodFunctionalInterface {

void count(int i);

String toString(); //same to Object.toString

int hashCode(); //same to Object.hashCode

boolean equals(Object obj); //same to Object.equals

}

函数式接口的抽象方法可以声明 受检查异常(checked exception)。 在调用目标对象的这个方法时必须catch这个异常。

public class FunctionalInterfaceWithException {

public static void main(String[] args) {

InterfaceWithException target = i -> {};

try {

target.apply(10);

} catch (Exception e) {

e.printStackTrace();

}

}

}

@FunctionalInterface

interface InterfaceWithException {

void apply(int i) throws Exception;

}

如果在Lambda表达式中抛出异常, 而目标接口中的抽象函数没有声明这个可检查.则不能在lambda表达式中抛出异常

public class FunctionalInterfaceWithException {

public static void main(String[] args) {

InterfaceWithException target = i -> {throw new Exception();};

}

}

@FunctionalInterface

interface InterfaceWithException {

void apply(int i);

}

静态方法

函数式接口是在JDK8后才引入的,而JDK8之后的接口可以添加公共静态公共方法。而函数式接口又是一种特殊的接口, 其既然是接口,当然也可以添加公共静态方法。

如下代码, 依然是公共静态方法

@FunctionalInterface

interface FunctionalInterfaceWithStaticMethod {

static int sum(int[] array) {

return Arrays.stream(array).reduce((a, b) -> a+b).getAsInt();

}

void apply();

}

public class StaticMethodFunctionalInterface {

public static void main(String[] args) {

int sum = FunctionalInterfaceWithStaticMethod.sum(new int[]{1,2,3,4,5});

FunctionalInterfaceWithStaticMethod f = () -> {};

}

}

默认方法

语法

default 数据类型 方法名(参数列表) {

}

默认方法解耦

默认方法也是JDK8引入的新特性。它主要为了解决JDK1.8之前,接口和类之前的耦合性问题, 在开发中当我们想给接口新的方法,其所有实现类也必须实现该接口,这样带来的代码改动很大。为了向后兼容, JDK8提供了default关键字,用该关键字修饰的方法,在接口中就提供了实现(所以才叫方法), 就是为了解决问题。

因为默认方法不是抽象方法,所以不影响我们判断一个接口是否是函数式接口

如下代码依然是一个函数式接口

@FunctionalInterface

interface InterfaceWithDefaultMethod {

void apply(Object obj);

default void say(String name) {

System.out.println("hello " + name);

}

}

class FunctionalInterfaceWithDefaultMethod {

public static void main(String[] args) {

InterfaceWithDefaultMethod i = (o) -> {};

i.apply(null);

i.say("default method");

}

}

@FunctionalInterface

该注解主要是为了来标注一个接口是函数式接口, 而且标注后可以让编译器来检查该接口是不是符合函数式接口的定义。作为开发者, 我们应该为每一个函数式接口添加上该注解.

规范, 提高代码可读性。

JDK8提供的新的函数式接口

java.util.function中定义了几组类型的函数式接口以及针对基本数据类型的子接口.

四大核心函数式接口

b30b294da48e

四大核心函数式接口

如上图根据这四种接口的定义我们可以写出其对应的lambda表达式

对于Consumer

()->{ statement };

对于Supplier

(t)->{ statement};

对于Fuction

(t)->{ R = statement; return R; };

对于Predicate

(t)->{ boolean r = statement;return r;}

其他函数式接口

b30b294da48e

其他函数式接口

这里简单介绍下BiFuncation和UnaryOperator接口, 其他其实大同小异.

BiFuncation接口规定了对应的lambda表达式是给定两个参数类型T和U, 经过计算返回R类型.

@Test

public void testBiFuncation() {

BiFunction concate = (o1, o2)-> o1 + "---" + o2;

System.out.println(concate.apply("hello", "Mr su")); // 输出hello---Mr su

}

BinaryOperator接口是BiFuncation的特殊例子, 其T, U,R都是同一种类型.

@Test

public void testBinaryOperator() {

BinaryOperator op = (o1, o2)-> o1 > o2? o1: o2;

Integer max = op.apply(2, 3);

System.out.println(max); // 输出3

}

UnaryOperator接口规定了参数只有一个且是T类型, 经过计算返回类型T

@Test

public void testUnaryOperator() {

UnaryOperator upper = (s)-> s.toUpperCase();

System.out.println(upper.apply("i love china"));

}

方法引用与构造器引用

方法引用

函数式接口可以直接引用已经实现好的方法, 只要该方法符合其规定的规范模式。有如下三种引用方式

对象::实例方法名

类名::静态方法名

类名::实例方法名

// 如System.out.println()就符合Consumer定义的模式.是消费型接口

// 给定一个数据就消费掉, 不返回.所以我们可以这样写

Consumer con = (s)->System.out.println(s);

Consumer con2 = System.out::println;

构造器引用

语法: 引用类型名::new

Funcation fun = MyClass::new;

数组引用

语法: 数组类型::new

Funcation fun = Integer[]::new;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值