文章目录
前言
java是面向对象语言,但是有时候为了更加方面的实现某些功能,引入了函数式编程,具体可见本文2.2中的例子。
本文首先介绍了函数式编程中必不可少的函数式接口,之后介绍了lambda表达式的基本格式、优点,以及常见的4个函数式接口。
1.函数式接口
- 有且仅有一个抽象方法的接口
格式:
修饰符 interface 接口名称 {
public abstract 返回值类型 方法名称(可选参数信息);
// 其他非抽象方法内容
}
举例:
注解:@FunctionalInterface
@FunctionalInterface
public interface MyFunctionalInterface {
void myMethod();
}
2.函数式编程
函数式编程中的函数指的不是程序中的函数(方法),而是数学中的函数即映射关系,如:y=sin(x),描述数据(函数)之间的映射,同时需要保证相同的输入始终能得到相同的输出。
函数式编程不会保留计算中间的结果,因此也可以说是无状态的。函数式编程可以将一个函数的执行结果再交由另一个函数去处理。
Java8通过Lambda表达式与方法引用等,实现函数式编程,可将其可定义为一种简洁、可传递的匿名函数。
2.1 Lambda表达式标准格式
- 两种格式:
(parameters) -> expression
(parameters) ->{ statements; }
- parameters:类似方法中的形参列表,这里的参数是函数式接口里的参数,多个参数逗号隔开
- ->:可以理解为被用于
- 方法体:
- List item
-
- 表达式,如果只有一句的话当前表达式即为返回值,不需要写return
-
- 代码:用{}括起来,如有返回值需要return
举例:
- 代码:用{}括起来,如有返回值需要return
// 返回给定字符串的长度(隐含return语句)
(String str) -> str.length()
// 始终返回233的无参方法(隐含return语句)
() -> 233
// 返回当前用户是否年龄大于20岁,返回一个boolean值(隐含return语句)
(User user) -> user.getAge() > 20
// 包含多行表达式,需用花括号括起来,并使用return关键字返回
(int x, int y) -> {
int z = x * y;
return x + z;
}
2.2 Lambda表达式举例
我们此处以通过Runnable接口实现类的创建线程为例,由于Runnable接口只有一个run()方法,所以这里从之前的直观写法到lambda表达式进行代码演示。
- 通过创建一个Runnable接口实现类来实现
public class TestThread {
@Test
public void testThread1(){
Thread thread = new Thread(new MyRunnable(), "thread1");
thread.start();
for (int i = 0; i < 100; i++) {
System.out.println("主线程" + i);
}
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread() + "正在执行:" + i);
}
}
}
- 通过匿名内部类
@Test
public void testThread2(){
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread() + "正在执行:" + i);
}
}
}, "thread1");
thread.start();
for (int i = 0; i < 100; i++) {
System.out.println("主线程" + i);
}
}
- lambda表达式
@Test
public void testThread3(){
Thread thread = new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread() + "正在执行:" + i);
}
}, "thread1");
thread.start();
for (int i = 0; i < 100; i++) {
System.out.println("主线程" + i);
}
}
2.3 函数式编程的好处
- 减少业务逻辑和代码的分歧。他允许我们在更高层次更自然的描述业务逻辑。让代码直接描述“你想做什么”,而不是“你想怎样去做”。
- 延迟执行:以lambda表达式为例,通过下面的例子可以看出lambda表达式是现将代码逻辑传送进去,符合逻辑才执行,此时传进去的flag为2,不符合逻辑,则lambda中的逻辑均不执行
public class TestThread {
@Test
public void testDelay(){
printLog(2, () -> {
System.out.println("lambda执行");
return "a" + "b" + "c";
});
}
private void printLog(int flag, MessageBuilder message){
if(flag == 1){
System.out.println(message.buildMessage());
}
}
}
interface MessageBuilder {
String buildMessage();
}
2.4 常用函数式接口
- 对于各个常用接口,这里仅介绍他们的抽象方法,对于Consumer等还有一些默认方法。
- 这四个接口,会通过后面的文章具体解析编程时的应用。
1. Supplier
对外提供”一个符合泛型类型的对象
数据。
@FunctionalInterface
public interface Supplier<T> {
T get();
}
举例:
import java.util.function.Supplier;
public class Demo08Supplier {
private static String getString(Supplier<String> function) {
return function.get();
} public static void main(String[] args) {
String msgA = "Hello";
String msgB = "World";
System.out.println(getString(() ‐> msgA + msgB));
}
}
2.Consumer
正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据,其数据类型由泛型决定
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
import java.util.function.Consumer;
public class Demo09Consumer {
private static void consumeString(Consumer<String> function) {
function.accept("Hello");
} public static void main(String[] args) {
consumeString(s ‐> System.out.println(s));
}
}
3.Predicate
对某种类型的数据进行判断,从而得到一个boolean值结果。
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
import java.util.function.Predicate;
public class Demo15PredicateTest {
private static void method(Predicate<String> predicate) {
boolean veryLong = predicate.test("HelloWorld");
System.out.println("字符串很长吗:" + veryLong);
} public static void main(String[] args) {
method(s ‐> s.length() > 5);
}
}
4.Function
用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
import java.util.function.Function;
public class Demo11FunctionApply {
private static void method(Function<String, Integer> function) {
int num = function.apply("10");
System.out.println(num + 20);
} public static void main(String[] args) {
method(s ‐> Integer.parseInt(s));
}
}