Java的函数式接口以及Lambda表达式

一、介绍

java中,大家肯定使用过lambda表达式吧,这是适用于函数式接口的一种便捷写法。

那么什么是函数式接口,简单点来说,一个接口中有且只有一个需要实现的方法,那么这个接口就是函数式接口

如果一个接口,你想定义为函数式接口,建议加上注解@Functionionallnterface,标注这个接口成为函数式接口,用来进行提示。

例如,多线程的Runnable接口就是一个函数式接口,如下

1
2
3
4
5
6
7
package java.lang;

@FunctionalInterface
public interface Runnable {

public abstract void run();
}

所以,我们在使用多线程时,可以使用lambda表达式进行简化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.banmoon.test;

import org.junit.jupiter.api.Test;

public class SomethingTest {

@Test
public void test() {
// 匿名内部类写法
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("匿名内部类写法");
}
}).start();

// lambda表达式,小括号是入参,大括号是函数式接口中唯一方法的实现
new Thread(() -> {
System.out.println("lambda表达式写法");
}).start();

// 方法实现只有单行时,可以再简写,省略大括号
new Thread(() -> System.out.println("lambda表达式写法")).start();
}

}

二、常用的函数式接口

1)Function<T, R>

先看源码,发现里面居然有四个方法。又仔细一看,确实里面仅有一个apply方法需要实现,所以Function也是一个函数式接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package java.util.function;

import java.util.Objects;

@FunctionalInterface
public interface Function<T, R> {

R apply(T t);

default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}

default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}

static <T> Function<T, T> identity() {
return t -> t;
}
}

这个函数式接口,接受有两个泛型,一个作为入参,一个作为出参

我第一时间就想到了转换类型,来看这个,从Integer转换为String,且内部做了一定的处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package com.banmoon.test;

import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

public class SomethingTest {

@Test
public void test() {
List<Integer> list = new ArrayList<Integer>(){{
add(7); add(5); add(1); add(2);
add(8); add(4); add(3); add(6);
add(3); add(6); add(3); add(6);
}};

Function<Integer, String> myFunction = new Function<Integer, String>() {
@Override
public String apply(Integer i) {
return "你好" + i;
}
};
List<String> stringList = list.stream().map(myFunction).collect(Collectors.toList());
System.out.println(stringList);

// 使用lambda表达式
List<String> stringList1 = list.stream().map((i) -> {
return "你好" + i;
}).collect(Collectors.toList());

// 再简写,当参数仅有一个时,小括号可以省略
stringList1 = list.stream().map(i -> {
return "你好" + i;
}).collect(Collectors.toList());

// 再简写,当实现的方法仅有一行语句时,大括号及return可以省略
stringList1 = list.stream().map(i -> "你好" + i).collect(Collectors.toList());
System.out.println(stringList1);
}

}

image-20220526160554725

看下stream接口中的这个map方法,需要我们传入一个Function接口的实现类对象,使用lambda表达式轻松实现对函数式接口的实现类对象的构建

1
2
3
4
5
6
package java.util.stream;

public interface Stream<T> extends BaseStream<T, Stream<T>> {

<R> Stream<R> map(Function<? super T, ? extends R> mapper);
}

2)Predicate<T>

先看源码,泛型只需要指定一个,需要实现的方法一个入参,出参是boolean,作用于判断的一个函数式接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package java.util.function;

import java.util.Objects;

@FunctionalInterface
public interface Predicate<T> {

boolean test(T t);

default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}

default Predicate<T> negate() {
return (t) -> !test(t);
}

default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}

static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}

来查看使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.banmoon.test;

import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class SomethingTest {

@Test
public void test() {
List<Integer> list = new ArrayList<Integer>(){{
add(7); add(5); add(1); add(2);
add(8); add(4); add(3); add(6);
add(3); add(6); add(3); add(6);
}};

Predicate<Integer> myPredicate = new Predicate<Integer>() {
@Override
public boolean test(Integer i) {
return i>5;
}
};

List<Integer> filterList = list.stream().filter(myPredicate).collect(Collectors.toList());
System.out.println(filterList);

// lambda表达式简化
filterList = list.stream().filter(i -> i > 5).collect(Collectors.toList());
System.out.println(filterList);
}

}

image-20220526164200162

看下stream接口中的这个filter方法,需要我们传入一个Predicate接口的实现类对象

1
2
3
4
5
6
package java.util.stream;

public interface Stream<T> extends BaseStream<T, Stream<T>> {

Stream<T> filter(Predicate<? super T> predicate);
}

3)Consumer<T>

看类名也能看出来,这是一个消费型的函数式接口,有入参,但没有返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package java.util.function;

import java.util.Objects;

@FunctionalInterface
public interface Consumer<T> {

void accept(T t);

default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}

使用如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.banmoon.test;

import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;

public class SomethingTest {

@Test
public void test() {
List<Integer> list = new ArrayList<Integer>(){{
add(7); add(5); add(1); add(2);
add(8); add(4); add(3); add(6);
add(3); add(6); add(3); add(6);
}};

Consumer myConsumer = new Consumer() {
@Override
public void accept(Object o) {
if(Objects.equals(o, 6))
return;
System.out.print(o);
}
};
list.forEach(myConsumer);

// lambda表达式简化
System.out.println("\n========== 分割线 ==========");
list.forEach(a -> {
if(Objects.equals(a, 6))
return;
System.out.print(a);
});
}

}

image-20220526170453349

查看forEach方法的源码,需要我们传入一个Consumer接口的实现类对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package java.lang;

import java.util.Iterator;
import java.util.Objects;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Consumer;

public interface Iterable<T> {

default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}

}

总有人问,为什么forEach方法不能使用breakcontinue。现在看了源码,你还有这样的疑问吗?

4)Supplier<T>

供给型接口,只有出参,没有入参。在使用上并不多见,先看源码

1
2
3
4
5
6
7
8
package java.util.function;

@FunctionalInterface
public interface Supplier<T> {

T get();
}

查看使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.banmoon.test;

import lombok.AllArgsConstructor;
import lombok.Data;
import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;

public class SomethingTest {

@Test
public void test() {
List<User> list = new ArrayList<User>() {{
add(new User("半月1", 18, 90));
add(null);
}};

Supplier<User> mySupplier = new Supplier() {
@Override
public Object get() {
return new User("未知", 0, 0);
}
};
list.forEach(a -> {
User user = Optional.ofNullable(a)
.orElseGet(mySupplier);
System.out.println(user);
});

System.out.println("========== 分割线 ==========");
// lambda表达式简化
list.forEach(a -> {
User user = Optional.ofNullable(a)
.orElseGet(() -> new User("未知", 0, 0));
System.out.println(user);
});
}

}

@Data
@AllArgsConstructor
class User {
private String name;
private Integer age;
private Integer score;
}

image-20220526175924101

查看Optional的源码,这个方法主要作用是,当前对象为null值后,将使用供给的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package java.util;

import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

public final class Optional<T> {

private final T value;

public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}

}

三、最后

上面例举的四个函数式接口,是比较经典的,在很多简化的代码中都可以看到他们的身影。

java8之后,lambda表达式出现,极大地提高了开发的效率,也使得java复杂臃肿的代码得到了缓解。

可谁知道,java8的发布时间在2013年9月份呢,距今都已经有9年的时间了,如果还不了解这上面这些接口,建议先使用Stream流处理。一段时间后,你会明白这些接口的。

使用stream的文章我也有写,可以参考进行学习,点击进行跳转

我是半月,祝你幸福!!!