函数式接口
什么是函数式接口:
接口中有且只有一个抽象方法则称为函数式接口。
- 所以函数式接口就是可以适用于Lambda使用的接口。
常见的函数式接口:
- Runnable
- Callable
- Comparator
- FileFilter
函数式接口的定义格式:
interface 接口名{
返回值类型 方法名(参数列表);
default 返回值类型 方法名(参数列表){
}
}
@Override注解作用:
用来修饰方法声明,告诉编译器该方法是重写父类的方法,如果父类没有该方法则编译失败。
@FunctionalInterface注解概述:
- JDK1.8新特性。
- 作用:用来修饰接口,告诉编译器该接口是函数式接口,如果不是则会编译失败。
自定义函数式接口(有参有返回)示例:
@FunctionalInterface
interface Sumable{
int sum(int a,int b);
}
public class FunctionalInterfaceDemo03 {
public static void main(String[] args){
// 使用lambda表达式调用
testSumable((x,y) ‐> x + y);
}
public static void testSumable(Sumable s){
int result = s.sum(1, 2);
System.out.println(result);
}
}
Lambda表达式
Lambda表达式的本质:
匿名函数。
Lambda表达式概述:
JDK1.8新特性,用来简化匿名内部类。
Lambda表达式的优点:
代码简介,只需告诉做什么。
Lambda表达式的思想:
只专注于做什么,而不是怎么做。
Lambda表达式的标准格式:
(参数列表) ‐> { 方法体 }
- 小括号就是方法的参数列表。
- ‐> 新语法,代表动作指向。
- 大括号就是方法体。
Lambda表达式的使用前提:
必须有接口且接口中有且只有一个抽象方法。
Lambda省略格式:
(参数列表) ‐> { 方法体 }
- 参数的数据类型可以省略。
- 如果参数列表中只有一个参数时,小括号可以省略。
- 如果方法体中只有一条语句时,大括号可以省略,如果省略了大括号则return语句和分号必须省略。(只写return后面的语句,不写return)
注意事项:
用Lambda表达式实现接口,要用东西来接,不接的话,系统就不知道你想实现哪个接口。
Lambda表达式的特点:
延迟执行。====> 需要的时候才会执行。可以处理性能浪费的问题。
示例代码:
public class Demo03LoggerDelay {
private static void log(int level, MessageBuilder builder) {
if (level == 1) {
System.out.println(builder.buildMessage());
}
}
public static void main(String[] args) {
String msgA = "Hello";
String msgB = "World";
String msgC = "Java";
log(2, () ‐> {
System.out.println("Lambda执行!");
return msgA + msgB + msgC;
});
}
}
从结果中可以看出,在不符合级别要求的情况下,Lambda将不会执行。从而达到节省性能的效果。
- 拓展:实际上使用内部类也可以达到同样的效果,只是将代码操作延迟到了另外一个对象当中通过调用方法来完成。而是否调用其所在方法是在条件判断之后才执行的。
Lambda表达式可以作为参数:
如果方法的参数是一个函数式接口类型,那么就可以使用Lambda表达式进行替代。使用Lambda表达式作为方法参数,其实就是使用函数式接口作为方法参数。
实例代码:
public class Demo04Runnable {
private static void startThread(Runnable task) {
new Thread(task).start();
}
public static void main(String[] args) {
startThread(() ‐> System.out.println("线程任务执行!"));
}
}
Lambda表达式可以作为返回值:
如果一个方法的返回值类型是一个函数式接口,那么就可以直接返回一个Lambda表达式。直接return一个Lambda表达式即可。
示例代码:
import java.util.Arrays;
import java.util.Comparator;
public class Demo06Comparator {
private static Comparator<String> newComparator() {
return (a, b) ‐> b.length() ‐ a.length();
}
public static void main(String[] args) {
String[] array = { "abc", "ab", "abcd" };
System.out.println(Arrays.toString(array));
Arrays.sort(array, newComparator());
System.out.println(Arrays.toString(array));
}
}
小结:
只要方法参数类型或返回值类型是函数式接口,则可以使用Lambda表达式作为方法参数或返回值。
函数式编程
概述:
使用Lambda表达式编程就是函数式编程。
方法引用
什么叫方法引用:
- JDK1.8新特性。
- 通过类名或对象名引用已经存在的方法来简化lambda表达式。
什么时候使用方法引用:
当Lambda表达式中仅仅调用了一个已经存在的方法时,可以考虑使用方法引用简化Lambda表达式。
方法引用的格式:
- 类名::方法名
- 对象名::方法名
方法引用的好处:
- 简化了lambda表达式。
- 可以重复利用已经存在的方法。
方法引用的原理:
- 创建了函数式接口的匿名内部类对象。
- 重写了函数式接口的抽象方法并在重写的方法中调用被引用的方法。
方法引用的类型:
- 静态方法引用:通过类名引用静态方法。
- 对象方法引用:通过对象名引用非静态方法。
- 构造方法引用:通过类名引用构造方法。
- 特定类型的实例方法引用。
方法引用之静态方法引用
静态方法引用的格式:
- 类名 :: 静态方法名
静态方法引用的注意事项:
- 被引用方法的参数列表要和函数式接口中抽象方法的参数列表一致。
- 如果函数式接口中的抽象方法中有返回值,则被引用的方法也必须有相同的返回值。
- 如果函数式接口中的抽象方法没有返回值,则被引用的方法可以有返回值,也可以没有返回值。
方法引用之对象方法引用
对象方法引用的格式:
对象名 :: 非静态方法
- 和静态方法的语法类似,只不过这里使用对象引用而不是类名。
- 对象在方法块外面创建。
对象方法引用的注意事项:
- 被引用方法的参数列表要和函数式接口中抽象方法的参数列表一致。
- 如果函数式接口中的抽象方法中有返回值,则被引用的方法也必须有相同的返回值。
- 如果函数式接口中的抽象方法没有返回值,则被引用的方法可以有返回值,也可以没有返回值。
实例:输出语句的方法引用
示例代码:
public class MethodRfDemo02 {
public static void main(String[] args){
// 创建集合对象
List<Person> persons = new ArrayList<>();
persons.add(new Person("李四",24));
persons.add(new Person("张三",23));
persons.add(new Person("老王",25));
// 遍历集合并输出元素
// persons.forEach( p ‐> System.out.println(p));
// 引用的方法是打印流对象的println方法。
persons.forEach(System.out::println);
}
}
方法引用之构造方法引用
构造方法引用格式:
类名 :: new
- 构造函数本质上是静态方法,只是方法名字比较特殊,使用的是new 关键字。
注意事项:
- 函数式接口中的抽象方法返回值是引用数据类型。
- 被引用的类必须存在一个构造方法的参数列表和函数式接口中的抽象方法参数列表相同。
方法引用之数组构造方法引用
数组构造方法引用格式:
数据类型[ ] :: new
示例代码:
@FunctionalInterface
public interface ArrayBuilder {
int[] buildArray(int length);
}
public class Demo12ArrayInitRef {
private static int[] initArray(int length, ArrayBuilder builder) {
return builder.buildArray(length);
}
public static void main(String[] args) {
int[] array = initArray(10, int[]::new);
}
}
方法引用之特定类型的实例方法引用
特定类型的示例方法引用格式:
类名 :: 非静态方法名
注意事项:
- 被引用的方法要比函数式接口中的抽象方法要少一个参数。
- 函数式接口中的抽象方法参数列表的第一个参数调用被引用的方法,抽象方法中的其他参数(除了第一个参数以外的其他参数)作为被引用方法的参数传递。
示例代码:
public class MethodRfDemo04 {
public static void main(String[] args){
String[] stringsArray = {"Barbara", "James", "Mary",
"John", "Patricia", "james", "Michael", "Linda"};
// 使用lambda表达式
// Arrays.sort(stringsArray,(s1,s2)‐> s1.compareToIgnoreCase(s2));
// 特定类型的实例方法引用
// 引用的是String类型的实例方法
Arrays.sort(stringsArray, String::compareToIgnoreCase);
System.out.println(Arrays.toString(stringsArray));
}
}
通过this引用成员方法
概述:
this代表当前对象,如果需要引用的方法就是当前类中的成员方法,那么可以使用this来使用方法引用。
格式:
this :: 成员方法
通过super引用成员方法
概述:
如果存在继承关系,当Lambda中需要出现super调用时,也可以使用方法引用进行替代。
格式:
super :: 父类成员方法
示例代码:
@FunctionalInterface
public interface Greetable {
void greet();
}
//父类
public class Human {
public void sayHello() {
System.out.println("Hello!");
}
}
//子类
public class Man extends Human {
@Override
public void sayHello() {
method(() ‐> super.sayHello());
}
private void method(Greetable lambda) {
lambda.greet();
System.out.println("I'm a man!");
}
}
//子类
public class Woman extends Human {
@Override
public void sayHello() {
method(super::sayHello);
}
private void method(Greetable lambda) {
lambda.greet();
System.out.println("I'm a woman!");
}
}