1.什么是函数式编程?
Java为函数式编程引入了三个新的语法概念:Stream类、Lambda表达式和函数接口(Functional Inteface)。 Stream类用来支持通过“.”级联多个函数操作的代码编写方式;引入Lambda表达式的作用是简化代码编写;函数接口的作用是让我们可以把函数包裹成函数接口,来实现把函数当做参数一样来使用(Java 不像C那样支持函数指针,可以把函数直接当参数来使用)
函数式编程不是我们平时在 Java 中编写的函数或者方法,它是一种针对于数学的概念,可以将其理解为一个表达式或者公式,或者理解为数据之间的转换关系。
- 在 JAVA8 之前,我们无法将一个函数作为参数传递给一个方法,也无法声明一个返回函数的方法。
-
JAVA8 对函数式编程做了哪些支持?
JAVA8 通过函数式接口和 Lambda 表达式为我们引入了函数式编程的概念。
1.1函数式编程的优劣
优点:
- 代码简洁可读性强,逻辑结构清晰。
- 线程安全,内部API屏蔽了coder对多线程的关注。
- 更好的复用性,往往一个函数适用于各种计算场景。
缺点:
- 由于函数内数据不变原则,导致的资源占用
- 调试上相对于命令式的困难
好处:
在java中,我们所谓的变量都是可以变化的,比如你设置一个变量为年龄,它可以按照我们的需求去更改这个年龄大小,但是 函数式编程中的变量跟jva中完全不同,它就相当于数学中的一个变量,那个值是不可以修改的,在函数式编程最终都会被编译成机器指令去允许,但是它不会因为调用的时间或者位置的变化而变化,函数的运行是独立在外部环境的,所以说它的最大的好处就是不可变。
然而就是因为这个不可变性,我们在进行多线程的时候就不需要额外枷锁,也不会再有各种线程安全问题。很适合来处理并发问题。
总而言之:函数式编程,写的更少,做的更多
2.函数式接口
在了解Lambda表达式之前,我们先要了解一些什么是函数式接口。因为在java中定义了一个承载Lambda表达式的接口,这个接口就是为函数式设计的接口,所以叫做函数式接口
这个接口封装了一个行为,不用传入参数,不用返回参数的行为,行为的具体实现也就是线程具体执行的任务需要调用者传入。这里就引入了一个概念,函数式接口是对某些行为的抽象,一个函数式接口只能有一个行为
通过函数式接口的定义我们看不出什么,到底什么是函数式接口,我们可以看源码里面的英文注释,这里我们直接给出定义。
- 函数式接口首先是一个接口。
- 函数式接口必须只有一个抽象函数。
- 除了一个抽象方法之外,函数式接口允许有默认方法和静态方法(接口中的默认方法和静态方法也是 JAVA8 引入的,下面的章节我们会详细讲解)。
- java.lang.Object 的抽象方法是不计算在内的,就是在函数式接口中可以定义许多 java.lang.Object 的抽象方法。
- 如果我们的接口满足上面的条件但是没有
@FunctionalInterface
注解依然会被编译器认作函数式接口。
既然我们定义了一个接口,那么我们在使用的时候肯定需要创建接口的实例,所以在函数式接口有三种实现方法,Lambda表达式,方法引用,构造方法 这三个方法来实现。
所以,Lambda 表达式的作用就是用来创建函数式接口的实例。
3.Lambda表达式
1.首先理解什么是Lambda表达式?
简单来说,我们定义一个函数,往往要去想怎么调用,怎么创建对象,但是Lambda表达式的思想是,我不用去管你怎么去做这件事情,我只关心你在做什么。我们只是为了达到目的,过程和形式并不重要。
2.Lambda优越性?
代码简洁,不用去写一大堆对象内部类,另一方面比较重要就是延时(会在下边举例)。
例如Runnable接口为例:
普通实现:
package test1;
public class test1 {
public static void main(String[] args) {
// 匿名内部类
Runnable task = new Runnable() {
@Override
public void run() { // 覆盖重写抽象方法
System.out.println("多线程任务执行!");
}
};
new Thread(task).start(); // 启动线程
}
}
Lambda实现代码
package test1;
public class test1 {
public static void main(String[] args) {
new Thread(() -> System.out.println("多线程任务执行!")).start(); // 启动线程
}
}
只需要一部即可完成。
3.Lambda的写法格式?
一个接口,只有一个抽象方法。
()->System.out.println("多线程任务执行!")看作是一个向上转型的对象就可以了,()代表无参,当然是可以加参数的,参数以逗号隔开,
System.out.println("多线程任务执行!")是你实现的抽象方法,在这里也就是run方法。
至于返回值,要看你对抽象方法的定义,该返回什么就返回什么,和普通方法没有区别。
4.接口里的方法
1.Supplier接口:又称为供应商,不传入参数,但是会返回
java.util.function.Supplier 接口仅包含一个无参的方法: T get() 。用来获取一个泛型参数指定类型的对 象数据。由于这是一个函数式接口,这也就意味着对应的Lambda表达式需要“对外提供”一个符合泛型类型的对象 数据。
方法 :T get()
2.Consumer接口:又称消费者,传入参数,但无返回
java.util.function.Consumer 接口则正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据, 其数据类型由泛型决定。
方法:因此它包含有一个有输入而无输出的accept接口方法,除accept方法,它还包含有andThen这个方法;
方法:(accept方法,andThen方法)
3.Predicate接口:有时候我们需要对某种类型的数据进行判断,从而得到一个boolean值结果,这时可以使用 java.util.function.Predicate 接口。
predicate的中文意思是“断定”,即判断的意思,判断某个东西是否满足某种条件; 因此它包含
test方法,根据输入值来做逻辑判断,其结果为True或者False。
方法:test
4.Function接口:
它代表的含义是“函数”,而函数经常是有输入输出的,因此它含有一个apply方法,包含一个输入与一个输出;
除apply方法外,它还有compose与andThen及indentity三个方法
方法:apply,compose,andThen , indentity