1. 函数式接口概述
函数式接口:有且仅有一个抽象方法的接口
Java中的函数式编程体现的就是Lambda表达式,所以函数式接口就是可以适用于Lambda使用的接口。
只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利的进行推导。
如何检测一个接口是不是函数式接口:
@FunctionalInterface
- 放在接口定义的上方:如果接口是函数式接口,编译通过;如果不是,编译失败
注意:自己定义函数式接口时@FunctionalInterface
是可选的,就算不写这个注解,只要保证满足函数式接口定义的条件,也照样是函数式接口;不过建议加上注解。
代码示例
- 接口
package Study01;
@FunctionalInterface//函数式接口注解
public interface MyInterface {
void show();
}
- 测试类
package Study01;
/*函数式接口:有且仅有一个抽象方法的接口*/
public class MyInterfaceDemo {
public static void main(String[] args) {
MyInterface my = ()-> System.out.println("我最帅");
my.show();
}
}
2.函数式接口作为方法的参数
需求:
- 定义一个类(
RunnableDemo
),在类中提供两个方法- 一个方法是
startThread(Runnable r)
方法参数Runnable
是一个函数式接口 - 一个方法是主方法,在主方法中调用
startThread
方法
- 一个方法是
如果方法的参数是一个函数式接口,我们可以使用Lambda表达式作为阐述传递。
-
startThread(()-> System.out.println(Thread.currentThread().getName()+"线程启动了"));
代码示例:
-
package Study02; /* 需求: - 定义一个类(RunnableDemo),在类中提供两个方法 - 一个方法是startThread(Runnable r)方法参数Runnable是一个函数式接口 - 一个方法是主方法,在主方法中调用startThread方法 */ public class RunnableDemo { public static void main(String[] args) { //在主方法中调用startThread方法 //匿名内部类的方法实现 startThread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + "线程启动了"); } }); //Lambda表达式 startThread(()-> System.out.println(Thread.currentThread().getName()+"线程启动了")); } public static void startThread(Runnable r) { Thread t = new Thread(r); t.start(); } }
3.函数式接口作为方法的返回值
需求:
- 定义一个类(
ComparatorDemo
),在类中提供两个方法- 一个方法是:
Comparator<String>getComparator()
方法返回值Comparator是一个函数式接口 - 一个方法是主方法,在主方法中调用
getComparator
方法
- 一个方法是:
注:如果方法的返回值是一个函数式接口,我们可以使用Lambda表达式作为结果返回。
代码示例:
ComparatorDemo
类
package Study02;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
/*
需求:
- 定义一个类(ComparatorDemo),在类中提供两个方法
- 一个方法是:Comparator<String>getComparator() 方法返回值Comparator是一个函数式接口
- 一个方法是主方法,在主方法中调用getComparator方法
*/
public class ComparatorDemo {
public static void main(String[] args) {
//构造场景
//定义集合
ArrayList<String> arrayList = new ArrayList<String>();
arrayList.add("cccc");
arrayList.add("aa");
arrayList.add("b");
arrayList.add("ddd");
System.out.println("排序前" + arrayList);//排序前[cccc, aa, b, ddd]
Collections.sort(arrayList);
System.out.println("排序后" + arrayList);//排序后[aa, b, cccc, ddd]
Collections.sort(arrayList, getComparator());
System.out.println("最后排序" + arrayList);//最后排序[b, aa, ddd, cccc]
}
private static Comparator<String> getComparator() {
//使用匿名内部类的方法实现
// Comparator<String> comparator = new Comparator<String>() {
// @Override
// public int compare(String s1, String s2) {
// return s1.length()-s2.length();
// }
// };
// return comparator;
//缩减版
// return new Comparator<String>() {
// @Override
// public int compare(String s1, String s2) {
// return s1.length()-s2.length();
// }
// };
//使用Lambda表达式
return (s1, s2) -> s1.length() - s2.length();
}
}
4.常用的函数式接口
Java 8
在java.util.function
包下预定义了大量的函数式接口
常用的有下面四个:
Supplier
接口Consumer
接口Predicate
接口Function
接口
1、Supplier
接口
Supplier:包含一个无参的方法
- T get():获得结果
- 该方法不需要参数,它会按照某种实现逻辑(由Lambda表达式实现)返回一个数据。
- Supplier接口也被称为生产型接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据供我们使用。
代码示例:
package Study02;
import java.util.function.Supplier;
/*
Supplier<T>:包含一个无参的方法
- T get():获得结果
- 该方法不需要参数,它会按照某种实现逻辑(由Lambda表达式实现)返回一个数据。
- Supplier<T>接口也被称为生产型接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据供我们使用。
*/
public class SupplierDemo {
public static void main(String[] args) {
// String s = getString(()->{
// return "我最帅";
// });
String s = getString(()->"我最帅");
System.out.println(s);
Integer i = getInteger(()->100);
System.out.println(i);
}
//定义一个方法,返回值是整数类型
private static Integer getInteger(Supplier<Integer> sup){
return sup.get();
}
//定义一个方法,返回值为字符串类型
private static String getString(Supplier<String> sup){
return sup.get();
}
}
练习:
- 定义一个类(
SupplierTest
),在类中提供两个方法- 一个方法是:
int getMax(Supplier <Integer> sup)
用于返回一个int
数组中的最大值。 - 一个方法是主方法,在主方法中调用
getMax
方法
- 一个方法是:
代码:
package Study02;
import java.util.function.Supplier;
/*练习:
- 定义一个类(`SupplierTest`),在类中提供两个方法
- 一个方法是:`int getMax(Supplier <Interger> sup)` 用于返回一个`int`数组中的最大值。
- 一个方法是主方法,在主方法中调用`getMax`方法
*/
public class SupplierTest {
public static void main(String[] args) {
//定义一个int类型的数组
int []arr = {19,22,33,44,11,1090};
//调用函数
int Max = getMax(()->{
//判断最大值并返回
int max = arr[0];
for (int i = 1;i<arr.length;i++){
if (arr[i]>max){
max = arr[i];
}
}
//输出最大值
return max;
});
System.out.println("数组中的最大值为:"+Max);
}
//返回一个`int`数组中的最大值。
private static int getMax(Supplier<Integer> sup) {
return sup.get();
}
}
2.Consumer
接口
Consumer:包含两个方法
- void accept(T t):对给定的参数执行此操作
- default ConsumerandThen(Consumer after):返回一个组合的Consumer,依次执行此操作,然后执行after操作。
- Consumer接口也被称为消费型接口,它消费的数据的数据类型由泛型指定。
代码:
package Study02;
import java.util.function.Consumer;
/*
Consumer<T>:包含两个方法
- void accept(T t):对给定的参数执行此操作
- default Consumer<T>andThen(Consumer after):返回一个组合的Consumer,依次执行此操作,然后执行after操作。
- Consumer<T>接口也被称为消费型接口,它消费的数据的数据类型由泛型指定。
*/
public class ConsumerDemo {
public static void main(String[] args) {
// operaterString("帅哥",(String s)->{
// System.out.println(s);
// } );
operaterString("帅哥",s-> System.out.println(s));
// operaterString("帅哥",System.out::println);
//实现字符串反转
// operaterString("帅哥",s->{
// System.out.println(new StringBuilder(s).reverse().toString());
// });
//改进
operaterString("帅哥",s-> System.out.println(new StringBuilder(s).reverse().toString()));
System.out.println("========================");
operaterString("帅哥",s-> System.out.println(s),s -> System.out.println(new StringBuilder(s).reverse().toString()));
}
//定义一个方法,用不同的方式消费同一个字符串两次
private static void operaterString(String name, Consumer<String> con1,Consumer<String> con2){
// con1.accept(name);
// con2.accept(name);
con1.andThen(con2).accept(name);
}
//定义一个方法,消费一个字符串数据
private static void operaterString(String name, Consumer<String> con){
con.accept(name);
}
}
练习:
- String[] strArray ={“迪丽热巴,18”,“王冰冰,18”,”古力娜扎,18”};
- 字符串中有多条信息,请按照格式:“姓名:xx,年龄:xx”的格式将信息打印出来
- 要求
- 把打印姓名的动作作为第一个Consumer接口的Lambda实例。
- 把打印年龄的动作作为第二个Consumer接口的Lambda实例。
- 将两个Consumer接口按照顺序组合到一起使用。
代码:
package Study02;
import java.util.function.Consumer;
/*
- String[] strArray ={"迪丽热巴,18","王冰冰,18","古力娜扎,18"};
- 字符串中有多条信息,请按照格式:“姓名:xx,年龄:xx”的格式将信息打印出来
- 要求
- 把打印姓名的动作作为第一个Consumer接口的Lambda实例。
- 把打印年龄的动作作为第二个Consumer接口的Lambda实例。
- 将两个Consumer接口按照顺序组合到一起使用。
*/
public class ConsumerTest {
public static void main(String[] args) {
String[] strArray = {"迪丽热巴,18", "王冰冰,18", "古力娜扎,18"};
operationString(strArray, (String str) -> {
String name = str.split(",")[0];
System.out.print("姓名:" + name);
}, (String str) -> {
int age = Integer.parseInt(str.split(",")[1]);
System.out.println(",年龄:" + age);
});
}
private static void operationString(String[] people, Consumer<String> con1, Consumer<String> con2) {
for (String peo : people) {
con1.andThen(con2).accept(peo);
}
}
}
3.Predicate
接口
Predicate<T>
:常用的四个方法
方法 | 说明 |
---|---|
boolean test(T t) | 对给定的参数进行判断(判断逻辑由Lambda表达式实现),返回一个布尔值 |
default Predicate<T> negate() | 返回一个逻辑否定,对应逻辑非 |
default Predicate<T>and(Predicate other) | 返回一个组合判断,对应短路与 |
default Predicate<T>or(Predicate other) | 返回一个组合判断,对应短路或 |
代码:
boolean test(T t)
和default Predicate<T> negate()
示例:
package Study03;
import java.util.function.Predicate;
/*
| boolean test(T t) | 对给定的参数进行判断(判断逻辑由Lambda表达式实现),返回一个布尔值 |
| default Predicate<T> negate() | 返回一个逻辑否定,对应逻辑非 |
*/
public class PredicateDemo {
public static void main(String[] args) {
// boolean b1 = checkString("hello world", (String s) -> {
// return s.length() > 8;
// });
boolean b1 = checkString("hello",(s)->s.length()>8);
System.out.println(b1);
boolean b2 = checkString("hello world",(s)->s.length()>8);
System.out.println(b2);
}
//判断给定的字符串是否满足要求
private static boolean checkString(String s, Predicate<String> pre) {
// return pre.test(s);
// return !pre.test(s);
return pre.negate().test(s);
}
}
default Predicate<T>and(Predicate other)
和default Predicate<T>or(Predicate other)
代码示例:
package Study03;
import java.util.function.Predicate;
/*
| default Predicate<T>and(Predicate other) | 返回一个组合判断,对应短路与 |
| default Predicate<T>or(Predicate other) | 返回一个组合判断,对应短路或 |
*/
public class PredicateDemo2 {
public static void main(String[] args) {
boolean b1 = checkString("hello", (s) -> s.length() > 8);
System.out.println(b1);
boolean b2 = checkString("helloworld", (s) -> s.length() > 8);
System.out.println(b2);
boolean b3 = checkString("hello",s->s.length()>8,s->s.length()<15);
System.out.println(b3);
boolean b4 = checkString("helloworld",s->s.length()>8,s->s.length()<15);
System.out.println(b4);
boolean b5 = checkString("helloworld",s->s.length()>15,s->s.length()>13);
System.out.println(b5);
}
//同一个字符串给出两个不同的判断条件,最后把两个判断的结果做逻辑与/或运算的结果作为结果
private static boolean checkString(String s, Predicate<String> pre1, Predicate<String> pre2) {
//逻辑与运算
// boolean b1 = pre1.test(s);
// boolean b2 = pre2.test(s);
// boolean b = b1 && b2;
// return b;
// return pre1.and(pre2).test(s);
//逻辑或运算
return pre1.or(pre2).test(s);
}
//判断字符串是否满足要求
private static boolean checkString(String s, Predicate<String> pre) {
return pre.test(s);
}
}
练习:
String[] strArray = {“王冰冰,18”,”迪丽热巴,19”,”天才小火龙,18”,”伤心排骨玉米汤,17”};
- 字符串数组中有多条信息,请通过
Predicate
接口的拼装将符合要求的字符串筛选到集合ArrayList
中,并遍历ArrayList
集合 - 同时按如下要求:姓名长度大于2,年龄小于20
代码:
package Study03;
import java.util.ArrayList;
import java.util.function.Predicate;
/*
- `String[] strArray = {“王冰冰,18”,”迪丽热巴,19”,”天才小火龙,18”,”伤心排骨玉米汤,17”};`
- 字符串数组中有多条信息,请通过`Predicate`接口的拼装将符合要求的字符串筛选到集合`ArrayList`中,并遍历`ArrayList`集合
- 同时按如下要求:姓名长度大于2,年龄小于20
*/
public class PredicateTest {
public static void main(String[] args) {
//定义并初始化集合
String[] strArray = {"王冰冰,18","迪丽热巴,19","天才小火龙,18","伤心排骨玉米汤,17","明磊,24"};
//调用checkString方法
ArrayList<String> arrayList = checkString(strArray,
s-> s.split(",")[0].length() > 2,
s -> Integer.parseInt(s.split(",")[1])<20);
//遍历输出
for (String str:arrayList){
System.out.println(str);
}
}
//通过`Predicate`接口的拼装将符合要求的字符串筛选到集合`ArrayList`中,并遍历`ArrayList`集合
private static ArrayList<String> checkString(String[] strArray, Predicate<String> pre1, Predicate<String> pre2){
//定义一个集合
ArrayList<String> arrayList = new ArrayList<String>();
//遍历数组
for (String str:strArray){
if (pre1.and(pre2).test(str)){
arrayList.add(str);
}
}
//返回集合
return arrayList;
}
}
4.Function
接口
Function<T,R>
:常用的两个方法
R apply(T t)
:将此函数应用于给定的参数defult<V> Function andThen(Function after)
:返回一个组合函数,首先将该函数应用于输入,然后将after函数应用于结果Function<T,R>
接口通常用于对参数进行处理,转换(处理逻辑由Lambda
表达式实现),然后返回一个新的值。
示例代码:
package Study04;
import java.util.function.Function;
/*
`Function<T,R>`:常用的两个方法
- `R apply(T t)`:将此函数应用于给定的参数
- `defult<V> Function andThen(Function after)`:返回一个组合函数,首先将该函数应用于输入,然后将after函数应用于结果
- `Function<T,R>`接口通常用于对参数进行处理,转换(处理逻辑由`Lambda`表达式实现),然后返回一个新的值。
*/
public class FunctionDemo {
public static void main(String[] args) {
//调用convert方法
convert("100", s -> Integer.parseInt(s));
//调用add方法
add(100, s -> String.valueOf(s + 100));
//调用ConvertAndAdd方法
ConvertAndAdd("100",s->Integer.parseInt(s),s->String.valueOf(s+200));
}
//定义一个方法,把一个字符串转换int类型,在控制台输出
private static void convert(String s, Function<String, Integer> fun) {
Integer i = fun.apply(s);
System.out.println(i);
}
//定义一个方法,把一个int类型的数据加上一个整数后,转为字符串在控制台输出
private static void add(int a, Function<Integer, String> add) {
String i = add.apply(a);
System.out.println(i);
}
//定义一个方法,把一个字符串转换为int类型,把int类型的数据加上一个整数后,转换为字符换在控制台输出。
private static void ConvertAndAdd(String s, Function<String, Integer> fun1, Function<Integer, String> fun2) {
String apply = fun1.andThen(fun2).apply(s);
System.out.println(apply);
}
}
练习:按照指定要求操作数据
- String s = “王冰冰,19”;
- 请按照指定的要求进行操作
- 将字符串截取得到数字年龄部分
- 将上一步的年龄字符串转换为int类型的数据
- 将上一步的int数据加1,得到一个int结果,在控制台输出
- 通过Function接口来实现函数拼接
package Study04;
import java.util.function.Function;
/*练习:按照指定要求操作数据
- String s = “王冰冰,19”;
- 请按照指定的要求进行操作
- 将字符串截取得到数字年龄部分
- 将上一步的年龄字符串转换为int类型的数据
- 将上一步的int数据加1,得到一个int结果,在控制台输出
- 通过Function接口来实现函数拼接
*/
public class FunctionTest {
public static void main(String[] args) {
String s = "王冰冰,18";
//Convert(s,s1->s1.split(",")[1],s2->Integer.parseInt(s2),s3->s3+1);
Convert(s,s1->s1.split(",")[1],Integer::parseInt,s3->s3+1);
}
private static void Convert(String s, Function<String, String> fun1, Function<String, Integer> fun2,Function<Integer,Integer>fun3) {
Integer apply = fun1.andThen(fun2).andThen(fun3).apply(s);
System.out.println(apply);
}
}