1 函数式编程
在数学中,函数就是有输入量、输出量的一套计算方案,也就是“用什么东西做什么事情”。相对而言,面向对象过分强调“必须通过对象的形式来做事情”,而函数式思想则尽量忽略面向对象的复杂语法——强调做什么,而不是以什么形式来做。
1.1 做什么,而不是怎么做
- 例如:
new Thread(new Runnable(){
@Override
public void run(){
//TODO things
}
}).start();
对于 Runnable
的匿名内部类用法,可以分析出几点内容:
-
Thread
类需要 Runnable
接口作为参数,其中的抽象 run 方法是用来指定线程任务内容的核心; - 为了指定 run 的方法体,我们必须要创建
Runnable
接口的实现类; - 为了省去定义一个
Runnable
实现类的麻烦,我们必须要使用匿名内部类; - 在匿名内部类中,我们必须覆盖重写抽象的 run 方法,所以方法名称、方法参数、方法返回值不得不再写一遍,且不能有错;
- 而实际上,大概仅有 run() 方法体才是程序中最需要的关键所在。
- 我们真正希望做的事情是,将 run 方法体内的代码传递给 Thread 类并去执行。
思考:
我们真的希望创建一个匿名内部类对象吗?不!
我们只是为了做这件事情而不得不创建一个对象。
1.2 函数式编程的本质是什么?
传递一段代码——这才是我们真正目的。而创建对象只是受限于面向对象语法而不得不采取的一种手段方式。那么,有没有更加简单的方法呢?
如果我们将关注点从“怎么做”回归到“做什么”的本质上,就会发现只要能够更好地达到目的,过程与形式其实并不重要。
匿名内部类存在的一个问题是,即使匿名内部类的实现非常简单,例如只包含一个抽象方法的接口,那么匿名内部类的语法仍然显得比较冗余。
解决方法:可以使用JDK8开始支持的 lambda 表达式,这种表达式只针对有一个抽象方法的接口实现,以简洁的表达式形式实现接口功能来作为方法参数。
new Thread(() -> System.out.println("多线程任务执行!")).start();
这段代码和刚才的执行效果是完全一样的,可以在1.8或更高的编译级别下通过。从代码的语义中可以看出:我们启动了一个线程,而线程任务的内容以一种更加简洁的形式被指定。
不再有“不得不创建接口对象”的束缚,不再有“抽象方法覆盖重写”的负担,就是这么简单!
1.3 Lambda表达式
lambda 表达式的基本语法格式
语句中通过箭头来区分开参数列表和方法体
2 函数式接口
2.1 是否可以使用 lambda 代替所有匿名内部类?
接口中有且只有一个抽象方法时,才能使用 lambda 表达式代替匿名内部类。这是因为 lambda 表达式是基于函数式接口实现的。
所谓函数式接口是指有且只有一个抽象方法的接口,lambda 表达式就是java中函数式编程的体现,只有确保接口中有且只有一个抽象方法,lambda 表达式才能顺利地推导出所实现的这个接口中的方法。
2.2 定义
在JDK8中,接口上标注有 @FunctionalInterface 注解的即为函数式接口,在函数式接口内部有且只有一个抽象方法。
来看下 Runnable 接口中的源码:
说明:@FunctionalInterface 注解只是显式的标注了接口是一个函数式接口,并强制编辑器进行更严格的检查,确保该接口是函数式接口。
JDK8之前已有的部分函数式接口:
- java.lang.Runnable
- java.util.Comparater
- java.io.FileFilter
- java.lang.Reflect.InvocationHandler
JDK8 增加的函数式接口
java.util.function 包下包含了很多类,用来支持java的函数式编程。
2.3 Lambda 表达式的简化
lambda 表达式的省略写法(进一步在lambda表达式的基础上继续简化)
1、如果lambda表达式的方法体代码只有一行代码,可以省略大括号不写,同时要省略分号!
2、如果lambda表达式的方法体代码只有一行代码,如果这行代码是return 语句,必须省略return 不写,同时省略分号。
3、参数类型可以省略不写。
4、如果只有一个参数,参数外的()也可以省略。
除了以上简化规则,还可以使用“方法引用”进一步简化 lambda 表达式。
public class TestOutter {
public static void main(String[] args) {
//匿名函数写法
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程1启动了!");
}
}).start();
//lambda写法
new Thread(() -> {System.out.println("线程2启动了!");}).start();
}
}
3 方法引用
说明:lambda 表达式的主体只有一条语句时,程序不仅可以省略包含主体的大括号,还可以通过英文双冒号“::”的语法格式来引用方法和构造器(即构造方法)。
作用:可以进一步简化lambda表达式的书写,其本质都是对 lambda 表达式的主体部分已存在的方法进行直接引用,主要区别就是对普通方法与构造方法的引用而已。
3.1 类名引用静态方法
定义:类名引用静态方法也就是通过类名对静态方法的引用,该类可以是java自带的特殊类,也可以是自定义的普通类。
引用示例如下:
//定义函数式接口
@FunctionalInterface
interface Calcable{
int calc(int num);
}
//定义一个类,类中定义一个静态方法
class Math{
public static int abs(int num){
return num > 0 ? num : -num;
}
}
public class Example {
private static void printAbs(int num, Calcable calcable){
System.out.println(calcable.calc(num));
}
public static void main(String[] args) {
//1、使用匿名内部类
printAbs(-8, new Calcable() {
@Override
public int calc(int num) {
return Math.abs(num);
}
});
//2、使用 lambda 表达式
printAbs(-8, (num) -> {return Math.abs(num);});
//3、简化 lambda 表达式
printAbs(-9, num -> Math.abs(num));
//4、使用方法引用,简化 lambda 表达式
printAbs(-7, Math::abs);
}
}
3.2 对象名引用方法
定义:对象名引用方法是指通过实例化对象的名称,来对其方法进行的引用。
引用示例如下:
//定义函数式接口
@FunctionalInterface
interface Utils{
String calc(String str);
}
//定义一个类,类中定义一个普通方法
class UpperUtil{
public String toUpper(String str){
return str.toUpperCase();
}
}
public class Example2 {
private static void printUpper(String ori, Utils u){
System.out.println(u.calc(ori));
}
public static void main(String[] args) {
UpperUtil upper = new UpperUtil();
//1、使用匿名内部类
printUpper("abc", new Utils() {
@Override
public String calc(String ori) {
return upper.toUpper(ori);
}
});
//2、使用 lambda 表达式
printUpper("bcd", (ori) -> {return upper.toUpper(ori);});
//3、简化 lambda 表达式
printUpper("cde", ori -> upper.toUpper(ori));
//4、使用方法引用,简化 lambda 表达式
printUpper("def", upper::toUpper);
}
}
3.3 构造器引用方法
定义:是指对类自带的构造器的引用。
引用示例如下:
//定义函数式接口
@FunctionalInterface
interface PersonBuilder{
Person buildPerson(String name);
}
//定义一个类,类中定义一个构造器及普通方法
class Person{
private String name;
public Person(String name){
this.name = name;
}
public String getName(){
return name;
}
}
public class Example3 {
private static void printName(String name, PersonBuilder buildPerson){
System.out.println(buildPerson.buildPerson(name).getName());
}
public static void main(String[] args) {
//1、使用 lambda 表达式
printName("李四", (String name) -> {return new Person(name);});
//2、使用构造器引用的方式
printName("张三", Person::new);
}
}
3.4 类名引用普通方法
定义:是指通过一个普通类的类名,来对其普通方法进行的引用。
引用示例如下:
//定义函数式接口
@FunctionalInterface
interface Printable{
void print(StringUtils su, String str);
}
//定义一个类及方法
class StringUtils{
public void printUppercase(String str){
System.out.println(str.toUpperCase());
}
}
public class Example4 {
private static void printUpper(StringUtils su, String str, Printable printable){
printable.print(su, str);
}
public static void main(String[] args) {
//1、使用匿名内部类
printUpper(new StringUtils(), "abc", new Printable() {
@Override
public void print(StringUtils su, String str) {
su.printUppercase(str);
}
});
//2、使用 lambda 表达式
printUpper(new StringUtils(), "bcd", (Object, t) -> Object.printUppercase(t));
//3、使用方法引用的方式
printUpper(new StringUtils(), "def", StringUtils::printUppercase);
}
}
标签:Java,String,编程,接口,面向对象,方法,public,表达式,lambda From: https://blog.51cto.com/u_113754/6052264