目录
基本概念
函数式接口:有且仅有一个抽象方法的接口
在java中,函数式编程的体现就是Lambda;Lambda可以被当作匿名内部类的“语法糖”,但是二者在原理上是不同的
@FunctionalInterface注解
作用:放在接口上,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错
Lambda会延迟执行:只有当调用接口对象时,Lambda才会被执行
使用Lambda表达式不会生成class文件
函数式编程
使用Lambda作为参数
public static void main(String[] args) {
new Thread(()-> System.out.println("线程启动")).start();
}
使用Lambda作为返回值(返回一个比较器)
private static Comparator<String> test1(){
return (a,b)->a.length()-b.length();
}
常用接口
Supplier接口
Supplier<T>接口被称之为生产型接口,指定接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据
没有形参、返回指定的数据类型数据
案例:求数组最大值
private static int max(Supplier<Integer> supplier){
return supplier.get();
}
public static void main(String[] args) {
int[] ints={1,2,3,11,33,133,22,5};
int max=max(()->{
int i=ints[0];
for (int anInt : ints) {
if(anInt>i){i=anInt;}
}
return i;
});
System.out.println(max);
}
Consumer接口
Consumer接口是一个消费型接口,泛型执行什么类型,就可以使用accept方法消费什么类型的数据至于具体怎么消费(使用),需要自定义(输出,计算....)
Consumer接口的默认方法andThen :需要两个Consumer接口,可以把两个Consumer接口组合到一起,在对数据进行消费 (一个数据 分别进行多次处理)
有形参、无返回值
案例:格式化打印
public static void test(ArrayList<String> arrayList, Consumer<String> consumer,Consumer<String> consumer1){
for (String s : arrayList) {
consumer.andThen(consumer1).accept(s);//同一个数据两次处理
}
}
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("张三,12");
list.add("李四,34");
test(list,(s)-> System.out.print("姓名:"+s.split(",")[0])
,(s)-> System.out.println("\t年龄:"+s.split(",")[1]));
}
Predicate接口
对某种数据类型的数据进行判断,结果返回一个boolean值
boolean test(T t):用来对指定数据类型数据进行判断的方法
有形参、返回boolean、用于逻辑判断
默认方法:and、or、negate就是(与或非)
示例代码
private static ArrayList<Integer>test_and(ArrayList<Integer>list, Predicate<Integer>predicate1,Predicate<Integer>predicate2){
ArrayList<Integer> arrayList = new ArrayList<>();//两种条件都符合时 数据进入新的集合 并且返回
for (Integer integer : list) {
if( predicate1.and(predicate2).test(integer)){
arrayList.add(integer);
}
}
return arrayList;
}
private static ArrayList<Integer>test_or(ArrayList<Integer>list, Predicate<Integer>predicate1,Predicate<Integer>predicate2){
ArrayList<Integer> arrayList = new ArrayList<>();//两种条件满足一个时 数据进入新的集合 并且返回
for (Integer integer : list) {
if( predicate1.or(predicate2).test(integer)){
arrayList.add(integer);
}
}
return arrayList;
}
private static void test_no( Predicate<String>predicate1){
System.out.println("条件求反:"+predicate1.negate().test("123345"));//非运算
}
public static void main(String[] args) {
ArrayList<Integer> arrayList = new ArrayList<>();
Collections.addAll(arrayList,1,32,11,49,2,4,123,49);
//大于10且小于40的数字
ArrayList<Integer> arrayList1 = test_and(arrayList, (i) -> i > 10, (i) -> i < 40);
System.out.println(arrayList1);//[32, 11]
//小于10或大于40的数字
ArrayList<Integer> arrayList2 = test_or(arrayList, (i) -> i < 10, (i) -> i > 40);
System.out.println(arrayList2);//[1, 49, 2, 4, 123, 49]
//非运算
test_no(s->s.length()>4);//条件求反:false
}
Function接口
java.util.function.Function<T,R>接口用来根据一个类型的数据得到另一个类型的数据, 前者称为前置条件,后者称为后置条件。
Function接口中最主要的抽象方法为:R apply(T t),根据类型T的参数获取类型R的结果。使用的场景例如:将String类型转换为Integer类型。
默认方法:andThen 用来进行组合操作。例如:A.andThen(B).apply(s):将s传入A,再将A返回的值传入B,最后返回B运算的值
主要应用于类型转换
示例代码:连续进行类型转换
private static int test(String s, Function<String,Integer> function1,Function<Integer,Integer>function2){
return function1.andThen(function2).apply(s);
}
public static void main(String[] args) {
test("12",(s)->Integer.parseInt(s),(i)->{
System.out.println(++i);
return i;
});
}
Stream流
以前对集合操作的时候,总是循环、循环、再循环。循环只是方式,不是目的。线性循环还有一个缺点只能遍历一次,如果还有其他操作还需要重新进行循环
Lambda的衍生物Stream(只考虑目的,不用考虑实现方式)
流式思想:
类似于流水线,一步一步的执行,中间的延迟方法都会返回一个流对象(流对象本身),最后会有终结方法
Stream流对象只能使用一次(每个模型(就是流对象)使用完后 都会自动销毁)
特点:
- Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道
- 内部迭代:Stream提供了内部迭代的方式,流可以直接调用遍历方法。
终结方法
- void forEach(Consumer<? super T> action); 接收每一个参数 对其处理
- long count();统计元素个数
延迟方法
- Stream<T> filter(Predicate<? super T> predicate);将一个流转换成子集流,进行过滤
- <R> Stream<R> map(Function<? super T, ? extends R> mapper);映射 可以将集合内的所有元素进行类型转换
- Stream<T> skip(long n);跳过前几个
- Stream<T> limit(long maxSize);取用前几个
静态方法
- static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b):将两个流合并到一起
获取流的方法
- 根据Collection获取流:stream方法
- 根据Map获取流:单独获取键值对的Set集合,再调用stream方法
- 根据数组获取流:Stream.of(数组对象)
示例代码
public static void main(String[] args) {
Integer[] ints={1,2,3,11,33,133,22,5};
String[] strings={"张三","李四","王五","赵六","张三丰"};
Stream<Integer> ints1 = Stream.of(ints);//获得stream对象
Stream<String> strings1 = Stream.of(strings);//获得stream对象
//使用过滤器 大于5小于150 最后输出
Stream<Integer> stream1 = ints1.filter(i -> i > 5).filter(i -> i < 150);//ints1已经使用过了 不能再一次使用
//姓张且长度大于2 的姓名
Stream<String> stream2 = strings1.filter(s -> s.startsWith("张")).filter(s -> s.length() > 2);
Stream<? extends Serializable> concat = Stream.concat(stream1, stream2);//进行拼接
concat.forEach(i-> System.out.print(i+"\t"));//11 33 133 22 张三丰
Stream<Integer> ints2 = Stream.of(ints);//获得stream对象
System.out.println();
//跳过前两个 取用前四个
ints2.skip(2).limit(4).forEach(i-> System.out.print(i+"\t"));//3 11 33 133
}
方法引用
方法引用是Lambda表达式的一种简写方法
Lambda 中 传递的参数 一定是方法引用中 的那个方法可以接收的类型
public static void main(String[] args) {
//printstring("HELLO",s-> System.out.println(s));
printstring("hello", System.out::println);
}
System.out::println等价于s-> System.out.println(s)
具体使用方法
- 通过对象名使用成员方法 对象名::成员方法名
- 通过类名称引用静态方法 类名::方法名 例如:Math::abs
- 通过super引用成员方法 supper::方法名
- 通过this引用成员方法 this::方法名
- 类的构造器引用 类名称::new 例如:name ‐> new Person(name)等价于Person::new
- 数组的构造器引用 length -> new int[length] 等价于int[]::new
示例代码
//父子之间的方法引用(子类调用带有函数式接口的方法,将参数传递给父类方法(无参的话之间调用父类的方法))
public class Zi extends Fu{
@Override
public void sey() {
System.out.println("我子类");
}
public void method(test1 t){
t.printaaa();
}
public void show(){
method(super::sey);//我是父亲
method(this::sey);//我子类
}
}
//类的构造器的引用
private static person buind(String name,int age,builder builder){
//接口功能获取个参数 建立对象
return builder.test2(name,age);
}
public static void main(String[] args) {
System.out.println( buind("王欢",12,person::new).toString());//person{name='王欢', age=12}
}
//数组构造器的引用
private static int[] binder(int l,test3 test3){
//接口的作用 接收一个参数(数组长度) 创建数组对象
return test3.num(l);
}
public static void main(String[] args) {
int[] binder = binder(12, int[]::new);
System.out.println(binder.length);//12
}