Supplier接口
java.util.function.Supperlier<T>
接口,它意味着“供给”,对应Lambda表达是需要“对外提供”一个符合泛型类型的对象数据
@FunctionalInterface
public interface Supplier<T>{
public abstract T get();
}
供给型接口,通过Supplier
接口中的get
方法可以得到一个值,无参有返回的接口
使用Lombda
表达式返回数组元素最大值
使用Supplier
接口作为方法参数类型,通过Lambda
表达式求出int数组中最大值,提示:接口的泛型请使用java.lang.Integer
类
代码实例:
public class Demo01Test(){
public static void main(){
printMax(() -> {
int[] arr = {
10,20,30};
//先排序,最后就是最大的
Arrays.sort(arr);//生序排序
return arr[arr.length - 1];//最后是最大的
})
}
public static void printMax(Suppelier<Integer> supplier) {
int max = supplier.get();
System.out.println("max=" + max );
}
}
Consumer接口
java.util.function.Consumer<T>
接口则正好相反,他不是生产一个数据,二是消费一个数据,其数据类型有泛型参数决定
@FunctionalInterface
public interface Consumer<T>{
public abstract void accpt(T t);
}
使用Lambda表达式将一个字符串转换成大写和小写的字符串
Consumer消费型接口,可以拿到accept方法参数传递过来的数据进行处理,有参返回的接口,基本使用如:
public class DemoConsumer(){
public static void main(String[] args){
//Lambda表达式 先将一个字符串转成小写字符串,在转成大写
test((String str) -> {
System.out.println(s.toLowerCase());//小写
},(String str) -> {
System.out.println(str.toUpperCase()); //大写
});
}
public static void test(Consumer<String> c1,Consumer<String> c2){
System.out.println("aa");
consumer.accept("HelloWord");
//c1.accept(str);//helloword
//c2.accept(str);//HELLOWORD
//调用Consumer这个接口的andThen方法 会执行两个,一个是自己的Consumer的accept方法,还有一个是after.accept()方法,先执行自己的方法,在执行after.accept()方法
c1.andThen(c2).accept(str);//先执行c1的accept方法,在执行c2的accept方法 效果等于上面两个注释的方法
}
}
执行效果图:
默认方法:andThen
如果一个方法的参数和返回值全都是Consumer
类型,那么可以就实现效果:消费一个数据的时候,首先做一个操作,然后在做一个操作,实现组合。而这个方法就是Consumer
接口中的default
方法andThen
消费是JDK的源代码
Function接口
java.util.funtion.Function<T,R>
接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。有参数有返回值。
@FunctionalInterface
public interface Function<T,R>(){
public abstract R apply(T,t);
}
使用Laambda表达式将字符串转成数字
Function转换型接口,对apply方法传入的T类型数据进行处理,返回R类型的结果,有参有返回的接口。使用的场景例如:将String
类型转换为Integer
类型
public class DemoFunction(){
public static void mian(String[] args){
//lambda表达式
只有一个Function参数的时候
/*test ((String s) -> {
return Integer.parseInt(s);//10
})*/
test((String str) -> {
return Integer.parseInt(s); //先把字符串的6转换成数字的6
},(Integer i) -> {
return i * 5; //在执行这个乘法操作输出结果
})
}
public static void test(Function<String,Integer> function, Function<Integer,Integer> f2){
Integer in1 = function.apply("10");
Integer in2 = f2.apply(num);
//System.out.println("in:" + (in + 5));
Integer in3 = function.andThen(f2).apply("6");
System.out.println("f2:" +f2);
}
}
结果
默认方法:andThen
Function
接口中有一个默认的andThen
方法,用来进行组合操作,JDK源码如:
Predicate接口
有时候我们需要对某种类型的数据进行判断,从而得到一个boolean值的结果,这是可以使用java.util.function.Predicate<T>
接口
@FucntionalInterface
public interface Predicate<T>{
piublic abstart boolean test(T t);
}
Predicate 接口用户判断,返回boolean类型的值 接口的泛型就是参数的泛型
使用Lambda判断一个人如果超过3个字就认为是很长的名字
对test方法的参数T进行判断,返回boolean类型的结果,用于条件判断的场景:
public class DemoPridicate {
//使用Lambda判断一个人如果超过3个就认为是很长的名字
public static void main(String[] args) {
isLongName((String name) ->{
return name.length() > 3 ;
});
}
public static void isLongName(Predicate<String> predicate){
System.out.println("aa");
boolean test = predicate.test("迪丽热巴");
System.out.println("是否是长名字:" + test);
}
}
判断条件的标准是传入的Lambda表达式逻辑,只要名称长度大于3则认为很长
## 默认方法:and
既然是条件判断。就会存在与,或,非三种常见的逻辑关系,其中将两个Predicate
条件使用“与”逻辑连接起来实现并且的效果时,可以使用default方法and
其JDK源码为:
default Predicate<T> and (Predicate<? super T> other){
Object.requreNonull(other);
return (t) ->(t) && other.test(t);
}
使用Lambda表达式判断一个字符串中既包含W,也包含H
使用Lambda表达式判断一个字符串中包含W或者包含H
使用Lambda表达式判断一个字符串中即不包含W
如果要判断一个字符串纪要包含大写H又不包含W
public class Demo13Pridicate {
//使用Lambda表达式判断一个字符串中既包含W,也包含H
//使用Lambda表达式判断一个字符串中包含W或者包含H
//使用Lambda表达式判断一个字符串中即不包含W
public static void main(String[] args){
test((String str) ->{
return str.contains("W");
},(String str) ->{
return str.contains("H");
});
}
public static void test(Predicate<String> p1, Predicate<String> p2){
/*String str = "Hello World";
boolean b1 = p1.test(str);
boolean b2 = p2.test(str);
if (b1 && b2){
System.out.println("既包含H,又包含W");
}*/
String str = "Hello World";
boolean test = p1.and(p2).test(str);
if (test){
System.out.println("既包含H,又包含W");
}
//使用Lambda表达式判断一个字符串中包含W或者包含H
boolean b = p1.or(p2).test(str);
if (b){
System.out.println("包含H或者包含W "+b);
}
//使用Lambda表达式判断一个字符串中即不包含W
boolean b2 = p1.negate().test("Hello World"); //negate()取反
if (b2){
System.out.println("不包含W");
}
}
}
方法引用
目标:
了解Lambda表达式
掌握方法引用的格式
了解常见的方法引用方式
Lambda的冗余场景
使用Lambda表达式求一个数组的和
public class DemoMethodReflct {
public static void getMax(int[] arr) {
int sum = 0;
for (int n : arr){
sum += n ;
}
System.out.println(sum);
}
public static void main(String[] args){
//使用Lambda表达式求一个数组的和
/* printMax((int[] arr) ->{
getMax(arr);
});
*/
//使用方法引用
// //让指定的方法重写接口的抽象方法,到时候调用接口的抽象方法就是调用传递过去的方法
printMax(DemoMethodReflct::getMax);
}
public static void printMax(Consumer<int[]> consumer ){
int[] arr = {
11,22,33,44,55};
consumer.accept(arr);
}
}
方法引用输出结果:
方法引用的格式
符号表示:::
符号说明:双冒号为方法运用运算符,而他所在的表达式被被称为方法引用
应用场景:如果Lambda所需要实现的方案,已经有其他方存在相同方案,那么则可以使用方法引用
常用的引用方式
方法引用在JDK 8 中使用方式相当灵活,有以下几种形式:
instanceName : : methodName
对象::方法名ClassName::staticMethodName
类名::静态方法ClassName::methodName
类名::普通的方法(特殊手段)ClassName::new
类名::new 调用的构造器TypeName[]::new
Sring[] :: new 调用数组的构造函数
对象名 :: 引用成员方法
这是最常见的一种用法,与上例相同,如果类中已经存在一个成员方法,即可以通过对象名引用成员方法,代码为:
//对象
@Test
public coid test01(){
Date now = new Date();
Supplier<Long> supp = () -> {
return now.getTime();
};
Supplier<Long> supp2 = now::getTime;
System.out.println();
]
方法引用注意事项
- 被引用的方法,参数要和接口中抽象方法的参数一样
- 当接口抽象方法有返回值时,被引用的方法也必须有返回值
lambda表达式相当于对这个抽象借口而的抽象方法的重写
类名::引用静态方法
由于java.lang.System
类中已经存在了静态方法currentTimeMillis
,所以当需要该方法时,可以使用方法引用,如下:
//类名:: 静态方法
@Test
public void test02(){
Supplier<Long> su = () ->{
return System.out.currentTimeMillis();
};
Long time = su.get();
System.out.println("time = "+ time);
}
类名::引用实例方法
由于构造器的名臣与雷鸣完全一样,所以构造器引用使用类名称:: new的格式表示。首先Person类
//类名::实例方法
@Test
public void test03(){
/* Function<String,Integer> function = (String str) ->{
return str.length();
};*/
//类名::实例方法(注意:类名::类名::实例方法实际上会降低一个参数作为方法的调用者)
Function<String,Integer> function = String::length;
int length =function.apply("hello");
System.out.println("length = "+length);
}
//吧substring方法赋给Biunction接口里面的抽象方法apply方法,apply才会调用这个函数
BiFunction<String,Integer,String> function1 = String::substring;
String hellowrod = function1.apply("hellowrod", 3);
System.out.println(hellowrod);//loworld
//类名::实例方法
@Test
public void test03(){
/* Function<String,Integer> function = (String str) ->{
return str.length();
};*/
//类名::实例方法
Function<String,Integer> function = String::length;
int length =function.apply("hello");
System.out.println("length = "+length);
//吧substring方法赋给Biunction接口里面的抽象方法apply方法,apply才会调用这个函数
BiFunction<String,Integer,String> function1 = String::substring;
//相当于这样的Lambda
BiFunction<String,Integer,String> function2 = (String str,Integer index) ->{
return str.substring(index);
};
System.out.println("lambda表达式"+function2.apply("helloworld",3));
String hellowrod = function1.apply("hellowrod", 3);
System.out.println(hellowrod);//loworld
}
```
**运行结果:**
![在这里插入图片描述](https://img-blog.csdnimg.cn/74b9f709d8b34f4e8da2804bf0b428aa.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAcXFfNTg3NDY0NzU=,size_15,color_FFFFFF,t_70,g_se,x_16)
### 类名::new应用构造器
由于构造器的名称与雷鸣完全不一样,所以构造器引用使用类名称::new 的格式表示,首先是一个简单的Person类:
```java
package com.jdk8methodref;
import org.junit.Test;
import java.util.function.BiFunction;
import java.util.function.Supplier;
public class DemoTest {
//类名::new 引用构造器
@Test
public void test04(){
/* Supplier<Person> supplier =() ->{
return new Person();
};*/
Supplier<Person> supplier = Person::new;
Person person = supplier.get();
System.out.println("person = "+person);
/*BiFunction<String,Integer,Person> biFunction = (String name,Integer age) -> {
return new Person(name,age);
};*/
BiFunction<String ,Integer,Person> biFunction = Person::new;
Person zhangsan = biFunction.apply("zhangsan", 18);
System.out.println(zhangsan);
}
}
执行效果:
数组::new 引用数组构造器
数组也是Object
的子类对象,所以同样具有构造器,只是语法稍有不同
//类型::new
@Test
public void test81(){
/*Function<Integer,int[]> function = (Integer length) ->{
return new int[length];
};*/
Function<Integer,int[]> function = int[]::new;
int[] arr = function.apply(10); //给int数组附一个10位的长度
System.out.println(Arrays.toString(arr));
}
小结
方法引用是对Lambda表达式符合特定情况下,他使得我们的Lambda表达式四更加的精简,也可以理解为Lambda表达式的缩写形式,不过要住的是方法只能“引用”已经存在的方法。
Stream流介绍
在Java 8 中,得益于Lambda所带来恩函数式编程,引入一种全新的Stream流概念,用于解决已有集合类库既有的弊端。
stream流式思想概述
注意:Stream和io流(inputStream/outputStream)没有任何关系,请暂时忘记对传统IO流的固有印象
Stream流式思想类似于工厂车间“生产流水线”,Stream流不是一种数据结构,不保存数据,而是对数进行加工处理,Sream可以看做是流水线上第一个工序,在流水线上,通过多个工序让一个原材料加工成一个商品。
对罐子里的材料进行逐步的加工处理,最后得到需要的数据
Stream API 能让我们快速完成许多复杂的操作,如筛选、切片、映射、查找、去重、统计、匹配和规约
public class DemoTest01 {
public static void main(String[] args) {
//一个ArrayList集合中存在一下数据:张无忌,周芷若,赵敏,张强,张三丰
//需求:1.拿到所有姓张的 2.拿到名字长度为3个字的 3.打印这些数据
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"张无忌","周芷若","赵敏","张强","张三丰");
//1.拿到所有姓张的 2.拿到名字长度为3个字的 3.打印这些数据
list.stream()
.filter((s) -> {
return s.startsWith("张");
})
.filter((s) ->{
return s.length() == 3;
})
.forEach((s) ->{
System.out.println(s);
});
System.out.println("------------------");
//1.拿到所有姓张的人
ArrayList<String> zhangList = new ArrayList<>();
for (String name:list) {
if (name.startsWith("张")){
zhangList.add(name);
}
}
//2.拿到名字为三个字的人
ArrayList<String> threeList = new ArrayList<>();
for (String name: zhangList) {
if (name.length() == 3){
threeList.add(name);
}
}
//3.打印这些数据
for (String name: threeList
) {
System.out.println(name);