一,lambad表达式
lambad表达式()->{}的引入 , ()内是参数,{}内是实现的细节
public static void main(String[] args) {
new Thread(() ->{
System.out.println("函数式编程思想,()内是参数,{}内是实现的细节");
// 箭头函数,lambda表达式 格式为 ()->{}
}).start();
}
一些用法:
在传入参数是接口的时候我们正常情况下是需要传入,接口或者接口的子实现类(多态思想的体现),但是引入lambda之后可以直接使用lambda之后可以直接使用这个表达式来调用接口中你需要的方法,接下来我会追入源码解析
代码如下
public static void main(String[] args) {
List<Bird> birds = new ArrayList();
birds.add(new Bird("blue", 15));
birds.add(new Bird("red", 4));
birds.add(new Bird("green", 18));
birds.add(new Bird("black", 1));
System.out.println("正序排列");
birds.sort((o1, o2) -> {
return o1.getAge() - o2.getAge();
});
System.out.println(birds);
System.out.println("倒序排列");
//todo 如果存在返回值 可以直接箭头指向返回值,return必须省略
birds.sort(((o1, o2) -> o2.getAge() - o1.getAge()));
System.out.println(birds);
}
运行结果如下:
接下来追入源码追入源码后可以看到他们二个都是这个方法而且都是需要传入一个Comparator接口
在举一个不同的案例帮助大家理解
public static void main(String[] args) {
// todo 函数式编制思想的引入 注意看下面俩排序函数中,都要求了传入一个参数为Comparator<? super E>
// 的接口或者子实现类,这个时候就可以直接用lambad函数表达式来直接调用,这个接口内的方法,具体的
// 方法细节需要看你用的的方法是什么下面截图会显示这些方法的内部细节
// lambda表达式的格式为 (参数)->{方法体} 注意()内的参数类型
List<Bird> list=new ArrayList<>();
list.add(new Bird("black",20));
list.add(new Bird("blue",11));
list.add(new Bird("yellow",35));
list.add(new Bird("red",55));
list.add(new Bird("green",5));
Bird[] bird=new Bird[5];
bird[0]=new Bird("blue",15);
bird[1]=new Bird("red",18);
bird[2]=new Bird("yellow",5);
bird[3]=new Bird("red",14);
bird[4]=new Bird("green",1);
System.out.println("bird数组排序得到的结果是");
Arrays.sort(bird,(o1,o2)-> o1.getAge()-o2.getAge());
System.out.println(Arrays.toString(bird));
System.out.println("list数组排序得到的结果是");
list.sort((o1,o2)->o1.getAge()-o2.getAge());
System.out.println(list);
}
这次只追入和上个案例不同方式的源码,供大家理解,另一个源码和上个案例一样不做演示了
追入sort源码:
和第一个案例同理
二,函数式接口
四大函数式接口
方法的四种形式,根据形式不同选择的方式不同 有参 有返回值 判断性接口(返回值只能是boolean类型) 其余返回值类型 用功能性接口 有参 无返回值 消费性接口 无参 有返回值 供给形接口 无参无返回值 这些函数式接口(接口中只有一个方法),只要是平时涉及到方法的四种类型,就都可以酌情根据上述图片的四大函数式接口类型来进行,筛选运用
代码演示
public static void main(String[] args) {
// 方法
// 有参 有返回值 判断性接口(返回值只能是boolean类型) 其余返回值类型 用功能性接口
// 有参 无返回值 消费性接口
// 无参 有返回值 供给形接口
// 无参无返回值
// todo 一旦运用了lambda表达式那么他就是一个方法,调用他只需要你四大函数式接口中的方法就会自动调用这个
// lambda表达式
//根据方法的返回值类型和参数类型的四种组合,可以直接调用 四大 函数式接口,来使用
// 调用的是
// 下面是 无参 有返回值 调用消费性接口
Supplier<String> supplier=()->new String[]{"石头","剪刀","布"}[(int)(Math.random()*3)];
System.out.println( supplier.get());
System.out.println( supplier.get());
System.out.println( supplier.get());
// 和不使用匿名函数相比,的代码简洁程度
System.out.println("普通方式的代码复杂程度");
String[] str={"石头","剪刀","布"};
System.out.println(str[(int) (Math.random()*3)]);
System.out.println(str[(int) (Math.random()*3)]);
System.out.println(str[(int) (Math.random()*3)]);
// 可以看到我们需要连续输入好几次重复的随机数获取 而使用lambda表达式就不会造成这种结果
// {1}
// {2,2}
// {3,3,3}
//有参有返回值 返回值部位 boolean返回 功能性接口
// 输入一个数字,这个数子就是你数组的大小以及需要填充的内容
Scanner scanner=new Scanner(System.in);
IntFunction<String> intFunction=(num) -> {
int [] arr=new int[num];
Arrays.fill(arr,num);
return Arrays.toString(arr);
};
// todo 这里num不声明或者传入 lambda表达式()内的原因是因为,在四大函数式接口中
// 已经定义了输入参数的类型,你只需要输入参数的名称,虚拟机会自动补充一个声明类型int
// 如果这里我的int num 声明在了()内 (int num) 内或者在外部声明然后传入()内
// 外部先声明int num 然后传入(num)都会导致 虚拟机再次给num声明一个int报错
// 会给你报错内容为重复定义 下面语句的报错信息和 我上述所说一样
//int sc;
//int int sc;
System.out.println(intFunction.apply(1));
System.out.println(intFunction.apply(2));
System.out.println("输入数字来决定他的长度和填充内容");
int num= scanner.nextInt();
System.out.println(intFunction.apply(num));
//判断输入的字符串 是否是英文和数字组成
Predicate<String> predicate=(string)->{
for (int i = 0; i < string.length(); i++) {
char c=string.charAt(i);
if (!((c>='a'&& c<='z') || (c>='A' && c<'Z') ||
(c>='0'&& c<= '9'))){
return false;
}
} return true;
};
System.out.println("输入字符串来判断你的字符串是否是数字和字母组成");
String string=scanner.next();
System.out.println(predicate.test(string));
}
三,lambda表达式的几种特殊形式
public static void main(String[] args) {
// lambad表达式的几种特殊情况
// 原本的书写格式()->{}
System.out.println("通过原本的书写格式来书写一个消费形接口(有参数,无返回)");
Consumer consumer=(str)->System.out.println(str);
consumer.accept("测试1");
consumer.accept("测试2");
consumer.accept("测试3");
// todo 特殊格式 特殊格式的原理是 如果我们传入的参数就是需要返回的类型,那么我们可以直接使用
// 类名::方法名 a::b 等价于 (参数)-> a.b(参数)
System.out.println("特殊格式");
Consumer consumer2= System.out::println;
consumer2.accept("test1");
consumer2.accept("test2");
consumer2.accept("test3");
System.out.println("==============案例二================");
// 这个也是有参数 有返回值的类型 但是需要传入的参数是 double 根据功能性接口,找到需要配对的函数式接口使用
DoubleUnaryOperator duo=Math::floor;
System.out.println( duo.applyAsDouble(1.2));
System.out.println( duo.applyAsDouble(2.4));
System.out.println( duo.applyAsDouble(9.9));
System.out.println("======================案例三====================");
// 有二个参数 返回一个int 功能性接口
IntBinaryOperator bo=Math::max;
System.out.println(bo.applyAsInt(1,3));
System.out.println( bo.applyAsInt(4,3));
}
四,自定义函数式接口
首先准备一个接口并且对接口使用注解@FunctionalInterface: 函数式接口内只能有一个方法!!
@FunctionalInterface
public interface Myinterface<A,B,C,D> {
A getMethod(B b,C c,D d);
}
public static void main(String[] args) {
// 对于对象创建使用lambda 创建可以调用无参也可以调用有参 返回的都是一个对象 ,
// 所以根据情况选择 是有参无返回值,还是无参有返回值 这里演示无参有返回值 下面自定义演示有参有返回值
Supplier s=Bird::new;
Bird bird= (Bird) s.get();
System.out.println("无参返回值的演示");
System.out.println(bird);
/** 自定义函数式接口 注意你定义的参数要和你调用的时候想照应,注意好每个参数的表达的意义
* 这里 A是返回值,BCD是内部的参数*/
Myinterface<Bird01,String,Integer,String> my=Bird01::new;
System.out.println("有参返回值的演示");
Bird01 bird01=my.getMethod("张三",18,"男");
System.out.println(bird01);
}
四,Stream API
特点 ①Stream 自己不会存储元素。 ②Stream 不会改变源对象。每次处理都会返回一个持有结果的新Stream。 ③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
步骤!1,创建 2,处理 3,关闭
public static void main(String[] args) {
// Stream API流的一些相关事项 Stream API ( java.util.stream) 把真正的函数式编程风格引入到Java中
// 注意:
//①Stream 自己不会存储元素。
//②Stream 不会改变源对象。每次处理都会返回一个持有结果的新Stream。
//③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
//todo 流的操作步骤 1,创建 2,处理 3,关闭(根据处理之后得到的流来调用方法返回想要的结果)
// 常用的几种创建方式
// 使用of创建
Stream stream= Stream.of(64,5,4,125,854,11,52);
// 处理流 注意每次处理都需要新的流来接收(可以用自己本身来接受)
Stream stream2=stream.sorted();
// 关闭流 关闭流的时候注意参数传入的要求,很多要求传入的是一个lambda表达式
stream2.forEach(System.out::println);
System.out.println("=============================");
int[] arr={5,6,45,32,22,49,52,2};
// 使用Arrays来创建一个流的同时处理并关闭,链式编程
Arrays.stream(arr)
.filter((num)->num<100)
.sorted()
.distinct()
.skip(2)
.forEach(System.out::println);
}