[Java][华为云Java编程创造营][学习笔记][第二阶段][05_Java面向对象之函数式编程]
【摘要】 1,面向函数式编程 1.1,函数式编程 函数式编程(1)在数学中,函数就是有输入量、输出量的一套计算方案,也就是"拿什么东西做什么事情",相对而言,面向对象过分强调"必须通过对象的形式来做事情",而函数式思想则尽量忽略面向对象的复杂语法,“强调做什么,而不是以什么形式做”。 函数式编程(2)做什么,而不是怎么做,例如:public class Test{ public static ...
1,面向函数式编程
1.1,函数式编程
函数式编程(1)
- 在数学中,函数就是有输入量、输出量的一套计算方案,也就是"拿什么东西做什么事情",相对而言,面向对象过分强调"必须通过对象的形式来做事情",而函数式思想则尽量忽略面向对象的复杂语法,“强调做什么,而不是以什么形式做”。
函数式编程(2)
- 做什么,而不是怎么做,例如:
public class Test
{
public static void main(String[] args)
{
new Thread(new Runnable()
{
@Override
public void run()
{
System.out.println("线程执行"); // 线程执行
}
}).start();
}
}
对于Runnable的匿名内部类用法,可以分析出几点内容:
- Thread类需要Runnable接口作为参数,其中的抽象run方法是用来指定线程任务内容的核心。
- 为了指定run的方法体,不得不需要Runnable接口的实现类。
- 为了省去定义一个RunnableImpl实现类的麻烦,不得使用匿名内部类。
- 必须覆盖重写抽象run方法,所以方法名称、方法参数、方法返回值不得不再写一遍,且不能写错。
- 而实际上,似乎只有方法体才是关键所在。
- 我们真正希望做的事情是:将run方法体内的代码传递给Thread类知晓。
- 我们真的希望创建一个匿名内部类对象吗?不!我们只是为了做这件事情而不得不创建一个对象。
函数式编程(3)
- 函数式编程的本质是什么?
- 传递一段代码,这才是我们的真正目的。而创建对象只是受限于面向对象语法而不得不采取的一种手段方式。那么,有没有更简单的方法?
- 如果我们将关注点从"怎么做"回归到"做什么"的本质上,就会发现只要能够更好地目的,过程与形式其实并不重要。
函数式编程(4)
- 匿名内部类存在的一个问题是,即便匿名内部类的实现非常简单,例如只包含一个抽象方法的接口,那么匿名内部类的语法仍然显得比较冗余。
- 解决方法:可以使用JDK1.8中新增的Lambda表达式,这种表达式只针对有一个抽象方法的接口实现,以简洁的表达式形式实现接口功能来作为方法参数。
public class Test
{
public static void main(String[] args)
{
new Thread(() -> System.out.println("多线程任务执行!")).start(); // 多线程任务执行!
}
}
- 这段代码和刚才的执行效果是完全一样的,可以在1.8或更高的编译级别下通过。从代码的语义中可以看出:我们启动了一个线程,而线程任务的内容以一种 更加简洁的形式被指定。
- 不再有"不得不创建接口对象"的束缚,不再有"抽象方法覆盖重写"的负担,就是这么简单。
1.2,函数式接口
Lambda表达式
- Lambda表达式基本语法格式
函数式接口(1)
是否可以使用Lambda代替所有匿名内部类?
- 接口中有且只有一个抽象方法时才能使用Lambda表达式代替匿名内部类。这是因为Lambda表达式是基于函数式接口实现的。
- 所谓函数式接口是指有且仅有一个抽象方法的接口,Lambda表达式就是Java中函数式编程的体现,只有确保接口中有且仅有一个抽象方法,Lambda表达式才能顺利地推导出所实现地这个接口的方法。
函数式接口(2)
- 定义:在JDK8中,接口上标注有@FunctionalInterface注解的即为函数式接口,在函数式接口内部有且仅有一个抽象方法。
- 说明:@FunctionalInterface注解只是显式的标注了接口是一个函数式接口,并强制编辑器进行更严格的检查,确保该接口是函数式接口。
函数式接口(3)
JDK1.8之前已有的一些函数式接口
- java.lang.Runnable
- java.util.concurrent.Callable
- java.security.PrivilegedAction
- java.util.Comparator
- java.io.FileFilter
- java.nio.file.PathMatcher
- java.lang.reflect.InvocationHandler
- java.beans.PropertyChangeListener
- java.awt.event.ActionListener
- javax.swing.event.ChangeListener
JDK1.8新增加的函数接口
- java.util.function 包下包含了很多类,用来支持Java的函数式编程。
1.3,方法引用
Lambda表达式的简化
- 复习:Lambda表达式的省略写法(进一步在Lambda表达式的基础上继续简化)
- 1,如果Lambda表达式的方法体代码只有一行代码,可以省略大括号不写,同时要省略分号
- 2,如果Lambda表达式的方法体代码只有一行代码,可以省略大括号不写,此时,如果这行代码是return语句,必须省略return不写,同时也必须省略";"不写。
- 3,参数类型可以省略不写。
- 4,如果只有一个参数,参数类型可以省略,同时()也可以省略。
- 除了以上简化规则,我们还可以使用"方法引用"进一步简化Lambda表达式。
方法引用(1)
- 说明:Lambda表达式的主体只有一条语句时,程序不仅可以省略包含主体的{}花括号,还可以通过英文双冒号"::"的语法格式来引用方法和构造器(即构造方法)。
- 作用:可以进一步简化Lambda表达式的书写,其本质都是对Lambda表达式的主体部分已存在的方法进行直接引用,主要区别就是对普通方法与构造方法的引用而已。
方法引用(2)
1,类名引用静态方法
- 定义:类名引用静态方法也就是通过类名对静态方法的引用,该类可以是Java自带的特殊类,也可以是自定义的普通类。
- 引用示例如下:
//定义一个函数式接口
@FunctionalInterface
interface Calcable
{
int calc(int num);
}
//定义一个类,并在类中定义一个静态方法
class Math
{
//定义一个求绝对值方法
public static int abs(int num)
{
if (num < 0)
{
return -num;
} else
{
return num;
}
}
}
public class Test
{
private static void printAbs(int num, Calcable calcable)
{
System.out.println(calcable.calc(num));
}
public static void main(String[] args)
{
//使用Lambda表达式方式
//IDE提示:Lambda can be replaced with method reference
printAbs(-10, n -> Math.abs(n)); // 10
//使用方法引用的方式
printAbs(-10, Math::abs); // 10
}
}
方法引用(3)
2,对象名引用方法
- 定义:对象名引用方法指的是通过实例化对象的名称来对其方法进行的引用。
- 引用示例如下:
//声明一个函数式接口
@FunctionalInterface
interface Printable
{
void print(String str);
}
class StringUtils
{
public void printUpperCase(String str)
{
System.out.println(str.toUpperCase());
}
}
public class Test
{
public static void printUpper(String text, Printable printable)
{
printable.print(text);
}
public static void main(String[] args)
{
StringUtils stringUtils = new StringUtils();
//使用Lambda表达式
printUpper("Hello", t -> stringUtils.printUpperCase(t)); //HELLO
//使用方法引用的方式
printUpper("Hello", stringUtils::printUpperCase); //HELLO
}
}
方法引用(4)
3,构造器引用方法
- 定义:构造器引用指的是对类自带的构造器的引用。
@FunctionalInterface
interface PersonBuilder
{
Person buildPerson(String name);
}
class Person
{
private String name;
public String getName()
{
return name;
}
public Person(String name)
{
this.name = name;
}
}
public class Test
{
public static void printName(PersonBuilder personBuilder, String name)
{
System.out.println(personBuilder.buildPerson(name).getName());
}
public static void main(String[] args)
{
printName(name -> new Person(name), "zhangsan"); // zhangsan
}
}
方法引用(5)
4,类名引用普通方法
- 定义:类名引用普通方法指的是通过一个普通类的类名来对其普通方法进行的引用。
//定义一个函数式接口
@FunctionalInterface
interface printTable
{
void print(StringUtils stringUtils, String string);
//void addfunc(); //增加一个方法时,IDE报错:Multiple non-overriding abstract methods found in interface com.huawei
}
class StringUtils
{
public void printUpperCase(String string)
{
System.out.println(string.toUpperCase());
}
}
public class Test
{
public static void printUpper(StringUtils stringUtils, String string, printTable printTable)
{
printTable.print(stringUtils, string);
}
public static void main(String[] args)
{
//使用Lambda表达式方式
printUpper(new StringUtils(), "Hello", (object, t) -> object.printUpperCase(t)); //HELLO
//使用方法引用的方式
printUpper(new StringUtils(), "Hello", StringUtils::printUpperCase); //HELLO
}
}
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)