概述
函数式编程
函数式接口作为方法的参数
函数式接口
Supplier接口
Consumer接口
andThen练习
Predicate接口
Functoin接口
小练习
函数式接口
概念
有且仅有一个抽象方法的接口(可以有其他类型方法,但是仅有一个抽象方法)
- Java中函数式接口体现就是lamda表达式
语法糖
是指使用更便捷,但是原理不变的代码语法,例如在遍历集合中使用for-each语法,但是代码底部使用迭代器实现
注意事项
- 使用lamda表达式比使用匿名内部类效率更高,以为不需要生成class文件
函数式编程
函数式接口作为方法的参数
/**
* 函数式接口:有且仅有一个抽象方法,称为函数式接口
* 当然可以有其它方法(默认方法,静态、私有等)
* @FunctionalInterface 作用
* 检测接口是否是一个函数式接口
* 是:编译成功
* 否:编译失败(接口中的抽象方法不是一个)
*/
public interface MyFunctionalInterface {
public abstract void method1();
//void method2();
}
/**
* @Override 注解
* 作用:
* 检测方法是否被重写
* 是:编译成功
* 否:编译失败
*/
public class MyFunctionalInterfaceImpl implements MyFunctionalInterface{
@Override
public void method1() {
}
}
/**
* 函数式接口的使用:一般可以作为方法的参数和返回值类型
*
*/
public class Demo01 {
/**
* 定义一个方法,参数使用函数式接口
*/
public static void show (MyFunctionalInterface myInter) {
myInter.method1();
}
public static void main(String[] args) {
//调用show方法,方法参数是一个接口,所以可以穿衣一个实现类对象
show(new MyFunctionalInterfaceImpl());
//调用show方法,使用匿名内部类
show(new MyFunctionalInterface() {
@Override
public void method1() {
System.out.println("使用匿名内部类重写接口中的抽象方法");
}
});
//使用lambda表达式重写
show(()-> System.out.println("使用lamda表达式重写抽象方法"));
}
}
/**
* java.lang.Runnable 接口是一个函数式接口
*
*/
public class Demo01Runnble {
/**
* 定义一个方法,参数为接口类型
*/
public static void startThread(Runnable run) {
//开启多线程
new Thread(run).start();
}
public static void main(String[] args) {
//调用startThread方法,参数为接口类型,传递实现类作为参数
startThread(()-> System.out.println(Thread.currentThread().getName() + "线程启动了"));
}
}
函数式接口
import java.util.Comparator;
/**
* 如果函数的返回值类型是一个函数式接口那么就可以直接返回一个lambda表达式
*/
public class Demo02Comparator {
/**
* 定义一个函数,函数的返回值类型为函数式接口
*/
public static Comparator<String> getComparator() {
// return new Comparator<String>() {
// @Override
// public int compare(String s, String t1) {
// return s.length() - t1.length();
// }
// }
/**
* 使用lambda表达式
*/
return (s,t1)-> s.length()-t1.length();
}
public static void main(String[] args) {
String[] arr = {"aaa", "aa", "lllll"};
System.out.println(Arrays.toString(arr));
//调用方法对字符串长度排序
Arrays.sort(arr, getComparator());
System.out.println(Arrays.toString(arr));
}
}
口作为方法的返回值类型
Supplier接口
- Supplier被称为生产型接口,指定接口的泛型是什么类型,那么接口中的get方法就会返回一个什么类型的数据
- java.util.function.Supplier 接口中仅包含一个无参的方法: T get().用来获取一个泛型类型指定的参数对象数据
import java.util.function.Supplier;
/**
* 常用函数式接口
* java.util.function.Supplier 接口中仅包含一个无参的方法: T get().用来获取一个泛型类型指定的参数对象数据
* Supplier被称为生产型接口,指定接口的泛型是什么类型,那么接口中的get方法就会返回一个什么类型的数据
*
*/
public class Demo01Supplier {
/**
* 定义一个方法,参数传递为接口类型
*/
public static String getString(Supplier<String> sup) {
return sup.get();
}
public static void main(String[] args) {
//调用方法,因为方法参数是一个函数式接口,以此可以传递lambda表达式
String s = getString(()->"好还有");
System.out.println(s);
}
}
import java.util.function.Supplier;
/**
* 练习:求数组元素的最大值
* 使用Supplier接口作为方法参数类型,通过lambda表达式求出最大值
*
*/
public class Demo02Test {
/**
* 定义一个方法,返回数组元素的最大值方法参数传递Supplier类型接口
*/
public static int getMax(Supplier<Integer> sup) {
return sup.get();
}
public static void main(String[] args) {
int[] arr = {6, 4, 2, 1, 78};
int maxValue = getMax(()-> {
int max = arr[0];
for (int e : arr) {
if (e > max) {
max = e;
}
}
return max;
}
);
System.out.println(maxValue);
}
}
Consumer接口
- Consumer接口是一个消费类型接口,泛型使用什么类型,就可以使用accept方法消费什么类型的数据
- java.util.function.Consumer 接口正好与Supplier接口相反
import java.util.function.Consumer;
/**
* java.util.function.Consumer<T> 接口正好与Supplier接口相反
* 他不是生产一个数据,而是消费一个数据,数据类型由泛型决定
* Consumer<T>接口是一个消费类型接口,泛型使用什么类型,就可以使用accept方法消费什么类型的数据
*
*/
public class Demo01Consumer {
/**
* 定义一个方法
* 方法的参数传递一个字符串姓名
* 方法的参数传递Consumer接口泛型使用String
* 可以使用Consumer接口消费字符串的姓名
*/
public static void method(String name, Consumer<String> con) {
con.accept(name);
}
public static void main(String[] args) {
//调用method方法,方法的参数传递字符串姓名,另一个参数是一个Consumer接口 ,可以使用lambda表达式
method("樊振东", (name)-> {
//消费方式:直接输出字符串
//System.out.println(name);
//消费方式:对字符串进行反转
String reName = new StringBuilder(name).reverse().toString();
System.out.println(reName);
});
}
}
import javax.crypto.spec.PSource;
import java.util.function.Consumer;
/**
* Consumer 接口的默认方法andThen
* 作用:需要俩个Consumer接口,可以把俩个Consumer接口组合到一起,再对数据进行消费
* 例如:
* Consumer<String> con1
* Consumer<String> con1
* String s = "hello";
* con1.accept(s);
* con2.accept(s);
* 连接来个Consumer接口,再进行消费
* con1.andThen(con2).accept(s); 谁先前面谁先消费
*/
public class Demo02AndThen {
/**
* 定义一个方法,方法的参数传递一个字符串和俩个接口
*/
public static void method(String s, Consumer<String> con1, Consumer<String> con2) {
// con1.accept(s);
// con2.accept(s);
//连接俩个Consumer接口,再进行消费(多个接口连接)
con1.andThen(con2).accept(s);
}
public static void main(String[] args) {
//调用method方法,传递一个字符串俩个lambda表达式
method("HellO", (t)-> {
//消费方法:转大写输出
System.out.println(t.toUpperCase());
}, (m)-> {
//消费方式:转小写输出
System.out.println(m.toLowerCase());
});
}
}
andThen练习
import java.util.function.Consumer;
/**
* 使用andThen打印姓名:年龄
* 要求将打印的姓名动作作为第一个Consumer接口的lambda实例
* 打印的性别动作作为第二个Consumer接口的lambda实例
* 将俩个Consumer接口按照顺序拼接到一起
*/
public class Demo03AndThenTest {
/**
* 定义一个方法,传递String类型数据和俩个Consumer接口,使用泛型String
*/
public static void printInfor(String[] arr, Consumer<String> con1, Consumer<String> con2) {
//遍历字符串数组
for (String str : arr) {
con1.andThen(con2).accept(str);
}
}
public static void main(String[] args) {
//定义一个字符串类型数组
String[] arr = {"樊振东,男", "马龙,男", "陈梦,女"};
printInfor(arr, (message)->{
//消费方式:以逗号作为分隔符分割
String name = message.split(",")[0];
System.out.println("姓名:" + name);
}, (message)-> {
//消费方式:以逗号作为分隔符获取性别
String sex = message.split(",")[1];
System.out.println("性别:" + sex);
});
}
}
Predicate接口
import java.util.function.Predicate;
/**
* java.util.function.Predicate<T> 接口
* 作用:对某种数据类型类型进行判断,结返回一个boolean值
* Predicate接口中包含一个抽象方法
* boolean test(T t) 对指定数据类型经进行判断
* 结果:
* 符合条件,返回true
* 否则:返回false
*/
public class Demo01Predicate {
/**
* 定义一个方法,传递一个String类型字符串,再传递一个接口
*/
public static boolean checkString(String str, Predicate<String> pre) {
return pre.test(str);
}
public static void main(String[] args) {
//定义一个字符串
String str = "好的嘛";
boolean ans = checkString(str, (s)->{
//对字符串长度是否大于5
if (s.length() > 5) {
return true;
}
return false;
});
System.out.println(ans);
}
}
import java.util.function.Predicate;
/**
* 逻辑表达式,可以连接多个判断条件
* && || !
* 需求:判断一个字符串,有俩个判断条件
* 1、判断字符串长度是否大于5
* 2、判断字符串中是否包含a
*
*/
public class Demo02PredicateAnd {
/**
* 定义一个方法,转递一个字符串,俩个接口
* 一个接口判断一个条件
*/
public static boolean checkString(String s, Predicate<String> pre1, Predicate<String> pre2) {
//return pre1.test(s) && pre2.test(s);
return pre1.and(pre2).test(s);
/**
* 若是或 return pre1.or(pre2).test(s);
*/
/**
* 对结果取反 return pre.negate().test(s);
*/
}
public static void main(String[] args) {
String s = "abcdfr";
boolean ans = checkString(s, (str)->{
//判断字符串长度是否大于5
return str.length() > 5;
}, (str)-> {
//判断是否包含a
return str.contains("a");
});
System.out.println(ans);
}
}
练习
import java.util.ArrayList;
import java.util.function.Predicate;
/**
* 要求:姓名长度必须小于4
* 必须为女
*/
public class Demo03Test {
/**
* 定义一个方法,传递一个字符出纳和俩二接口
*/
public static ArrayList<String> checkString(String[] arr, Predicate<String> pre1, Predicate<String> pre2) {
//定义一个集合存储过滤之后的信息
ArrayList<String> list = new ArrayList<>();
//遍历字符串数组
for (String str : arr) {
//判断是否满足上述条件
boolean flag = pre1.and(pre2).test(str);
//满足条件加入集合
if (flag) {
list.add(str);
}
}
return list;
}
public static void main(String[] args) {
String[] arr = {"迪丽热巴,女", "杨静,男", "康辉,男", "尼格买提,女", "妮妮,女"};
//调用过滤方法
ArrayList<String> list = checkString(arr, (str)-> {
//名字长度必须小于4
String name = str.split(",")[0];
return name.length() < 4;
}, (str)-> {
//是否为女
String sex = str.split(",")[1];
return sex.equals("女");
});
System.out.println(list);
}
}
Function接口
- java.util.function.Function<T,R>接口可以用来根据一个类型的数据的得到另一个类型的数据
- 前者称为前置条件,后者称为后置条件
import java.util.function.Function;
/**
* java.util.function.Function<T,R>接口可以用来根据一个类型的数据的得到另一个类型的数据
* 前者称为前置条件,后者称为后置条件
* Function接口最主要的抽象方法为:R apply<T t> 根据类型T的参数,获取R类型的结果
* 使用场景:如将String类型转换为Integer类型
*/
public class Demo01Function {
/**
* 定义一个方法
* 参数传递一个字符串类型整数
* 方法的参数传递一个Function接口,泛型使用<String, Integer>
* 使用Function接口中的方法apply,把字符串类型的整数转换为Integer类型
*/
public static void change(String s, Function<String, Integer> fun) {
Integer in = fun.apply(s); //自动拆箱
System.out.println(in);
}
public static void main(String[] args) {
String s = "124";
change(s, (str)-> {
return Integer.parseInt(s);
});
}
}
andThen方法
- Function 接口中默认方法andThen
- 作用:将分解的步骤连接在一起
import java.util.function.Function;
/**
* Function 接口中默认方法andThen
* 作用:将分解的步骤连接在一起
* 需求:把String类型123转换为Integer类型,将转换后的结果加10
* 把增加后的 结果转换为Integer类型
* 转化了俩次:
* 第一次:Integer->String类型
* 第二次:String->Integer类型
*/
public class Demo01AndThen {
/**
* 定义一个方法
* 参数一个为字符串,另外俩个为接口
*/
public static void change(String s, Function<String, Integer> fun1, Function<Integer, String> fun2) {
//将字符串类型转换为Integer类型再转换为字符串类型
String str = fun1.andThen(fun2).apply(s);
System.out.println(str);
}
public static void main(String[] args) {
//定义一个字符串类型整数
String num = "123";
//调用change方法,传递字符串和俩个lambda表达式
change(num, (str)-> {
//将字符串类型转换为整数类型再加上10
return Integer.parseInt(str) + 10;
}, (s)-> {
return s+"";
});
}
}
练习
import java.util.function.Function;
/**
* 练习:自定义函数拼接模型
* String str = "赵丽颖,20";
* 分析:
* 1、截取字符串年龄部分
* 2、将字符串类型转换为Integer类型
* 3、将上一步的int数字累加100
*/
public class Demo02Test {
/**
* 定义i一个方法:
* 参数传递一个字符串,三个接口
*/
public static void change(String s, Function<String, String> fun1, Function<String, Integer> fun2, Function<Integer, Integer> fun3) {
//把三个组合到yiqi
int ans = fun1.andThen(fun2).andThen(fun3).apply(s);
System.out.println(ans);
}
public static void main(String[] args) {
//定义一个字符串
String s = "赵丽颖,20";
//调用change方法,长短地一个字符串,三个接口
change(s, (str)-> {
//截取年龄部分
String age = str.split(",")[1];
return age;
}, (str2)-> {
//将字符串类型转换为int类型
return Integer.parseInt(str2);
}, (num)-> {
//将结果加上100
return num+100;
});
}
}