JDK8新特性-函数式接口
1.函数式接口
1.1函数式接口的概念及定义
概念:
有且只有一个抽象方法的接口,就是函数式接口
当然可以有静态方法或私有方法
定义:
public interface 接口名称 {
public abstract 返回值类型 方法名称();
}
1.2@FunctionalInterface
该注解用于标注函数式接口
作用:检测是否是函数式接口
如果是:编译成功
如果不是:编译失败(报错)
1.3实现函数式接口的三种方式
1.3.1编写实现类
public class MyFunctionInterfaceImpl implements MyFunctionInterface {
@Override
public void show() {
System.out.println("hello!!");
}
}
1.3.2匿名内部类
MyFunctionInterface mfi = new MyFunctionInterface() {
@Override
public void show() {
System.out.println("hello 匿名内部类!!");
}
};
mfi.show();
1.3.2lambda表达式
MyFunctionInterface mfi = ()->{
System.out.println("hello lambda!!");
};
mfi.show();
1.4lambda表达式介绍
作用:lambda表达式可以实现函数式接口中的方法
1.4.1lambda表达式的使用场景:
lambda表达式只能在函数式接口的支持下使用(接口只有一个抽象方法)
1.4.2lambda表达式的语法格式:
lambda表达式被 -> 箭头符号分成左右两部分
左边():代表的就是函数式接口中的参数列表
右边 {}: 代表我要实现的方法的方法体
1.语法格式一:只有一个参数
(String msg)->{
System.out.println(msg);
};
2.语法格式二:多个参数
(String name,int age)->{
System.out.println("姓名:"+name+",年龄:"+age);
};
3.语法格式三:带有返回值
(String name,int age)->{
return "姓名:"+name+",年龄:"+age;
};
4.语法格式四:参数的类型可以省略,因为jdk可以自动类型推断
(msg)->{
System.out.println(msg);
};
5.语法格式四:如果参数只有一个可以省略()
msg->{
System.out.println(msg);
};
6.语法格式五:如果方法体中只有一条语句,{},return也是可以省略的
//省略{}
msg->System.out.println(msg);
//省略return语句
(name,age)->"姓名:"+name+",年龄:"+age;
1.5JDK中提供的常用的函数式接口
1.5.1.Interface Supplier
位于java.util.function 包下,称之为生产型接口
通常给该接口的泛型指定什么类型,通过get方法就可以返回该类型的数据。
@FunctionalInterface
public interface Supplier<T> {
T get();
}
需求:使用Supplier接口作为方法的参数,求数组的最大值
public static Integer getMax(Supplier<Integer> supplier){
return supplier.get();
}
public static void main(String[] args) {
int maxVal = getMax(()->{
//计算数组的最大值
int[] arr = new int[]{2,80,38,20};
int max = arr[0];
for (int i = 0; i < arr.length; i++) {
if(arr[i]>max){
max=arr[i];
}
}
return max;
});
System.out.println(maxVal);
}
1.5.2.Interface Consumer
位于java.util.function 包下,该接口是一个消费型的接口
public interface Consumer<T> {
void accept(T t);
}
可以通过accept方法消费泛型指定的类型。
需求:使用Consumer接口作为方法的参数,对字符串实现翻转
public class TestFunInterface2 {
//需求:使用Consumer接口作为方法的参数,对字符串实现翻转
public static void revString(String str, Consumer<String> consumer){
consumer.accept(str);
}
public static void main(String[] args) {
revString("zhangsan",(str)->{
StringBuilder reverseStr = new StringBuilder(str).reverse();
System.out.println("翻转后的字符串:"+reverseStr);
});
}
}
默认方法:andThen()
该方法作用将两个Consumer接口连接在一起,然后在进行消费
public class TestFunInterface3 {
public static void method(String s, Consumer<String> con1,Consumer<String> con2){
//con1.accept(s);
//con2.accept(s);
con1.andThen(con2).accept(s); //谁在前面谁先消费
}
public static void main(String[] args) {
method("zhangsan",(str)->{
System.out.println(str.toUpperCase());
},(str)->{
System.out.println(str.toLowerCase());
});
}
}
1.5.3.Interface Predicate
位于java.util.function 包下,主要是用来进行一个判断,
通过test方法进行判断,如果成功,返回true,如果失败返回false;
public interface Predicate<T> {
boolean test(T t);
}
需求:使用Predicat接口作为方法的参数,实现对一个字符的长度进行判断
public class TestFunInterface4 {
//需求:使用Predicat接口作为方法的参数,实现对一个字符的长度进行判断
public static boolean checkStr(String s, Predicate<String> predicate){
return predicate.test(s);
}
public static void main(String[] args) {
boolean result = checkStr("zha", (str) -> {
return str.length() > 5;
});
System.out.println(result);
}
}
逻辑运算符
&&:与
||:或
!:非
默认方法and()、or()、negate()
and() :等价&&
or() :等价 ||
negate() :等价 !
需求:判断一个字符串的长度是否大于5,并且判断该字符串中是否包含a
public class TestFunInterface5 {
//需求:判断一个字符串的长度是否大于5,并且判断该字符串中是否包含a
/*分析:1.与 操作,可以使用&& 或使用and()方法去实现
2.我们可以传递两个Predicate类型的参数,
第一个参数用来判断字符串的长度是否大于5
第一个参数用来判断该字符串中是否包含a
*/
public static boolean checkString(String s,
Predicate<String> predicate1,
Predicate<String> predicate2){
//predicate1.test(s)&&predicate2.test(s);
return predicate1.and(predicate2).test(s);
}
public static void main(String[] args) {
boolean b = checkString("zha",(s)->{
return s.length()>5;
},(s)->{
int i = s.indexOf("a");
if(i>0){
return true;
}else{
return false;
}
});
System.out.println(b);
}
}
1.5.4.Interface Function<T,R>
位于java.util.function 包下,转化接口,两个类型的泛型,
根据一种数据类型得到另一种类型的数据
public interface Function<T, R> {
R apply(T t);
}
需求:将String类型的整数转化成int类型的整数
public class TestFunInterface6 {
//需求:将String类型的整数转化成int类型的整数
public static Integer convert(String a,
Function<String,Integer> function){
return function.apply(a);
}
public static void main(String[] args) {
int a = convert("123",(s)->{
return Integer.parseInt(s);
});
System.out.println(a);
}
}
默认方法:andThen()
该方法作用将两个Function接口连接在一起
需求:将String类型的数据转换成Integer类型,加10之后,再将其转换成String类型
public class TestFunInterface7{
//需求:将String类型的数据转换成Integer类型,加10之后,再将其转换成String类型
/*
* 分析:
* 涉及到两次转换:
* 1.String类型的数据转换成Integer类型
* 使用Function接口进行转换 Integer apply(String)
* 2.加10之后,再将其转换成String类型
* 使用Function接口进行转换 String apply(Integer)
* 使用andThen()方法来连接这两个Function接口
*/
public static String convert(String s, Function<String,Integer> fun1,
Function<Integer,String> fun2){
return fun1.andThen(fun2).apply(s);
}
public static void main(String[] args) {
String str = convert("123",(s)->{
return Integer.parseInt(s)+10;
},(i)->{
return i.toString();
});
System.out.println(str);
}
}
2.Stream流
1.简介
流(Stream)到底是什么呢?
是数据渠道,**用于操作数据源(集合、数组等)**所生成的元素序列。
注意:
Stream自己不会存储元素。
Stream不会改变源对象。相反,他们会返回一个持有结果的新 Stream
Stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
延迟执行:多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。
2.三个步骤
-
创建 Stream流
一个数据源(如:集合、数组),获取一个流
1.通过Collection系列集合提供的stream()
2.通过Arrays中的静态方法stream()获取数组流
3.通过Stream类中的静态方法of()
-
中间操作
一个中间操作链,对数据源的数据进行处理
-
终止操作(终端操作)
一个终止操作,执行中间操作链,并产生结果
3.中间操作-筛选与切片
filter: 接收 Lambda,从流中排除某些元素。
limit(n) : 截断流,使其元素不超过给定数量n。
skip(n) : 跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n)互补
distinct : 筛选,通过流所生成元素的 hashCode()和 equals()去除重复元素
public static void main(String[] args) {
//filter : 查询年龄小于等于20岁的员工信息
/*Stream<Employee> stream1 = employees.stream()
.filter((employee) -> {
System.out.println("中间操作");
return employee.getAge() <= 20;
});*/
//limit: 限定返回两条数据
Stream<Employee> stream1 = employees.stream()
.filter((employee) -> {
System.out.println("中间操作");
return employee.getAge() <= 20;
})
.limit(2);//做了一个短路的处理
stream1.forEach((e)->{
System.out.println(e);
});
}
4.中间操作-映射
map :接收 Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap : 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
public static void main(String[] args) {
//**map** :接收 Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,
// 该函数会被应用到每个元素上,并将其映射成一个新的元素。
//提取员工的姓名
employees.stream()
.map((employee)->{
return employee.getName();
})
.forEach((e)->{
System.out.println(e);
});
}
5.中间操作-排序
sorted() : 自然排序
sorted(Comparator com) : 定制排序
public static void main(String[] args) {
//**sorted()** : 自然排序 int类型、String类型
//**sorted(Comparator com)** : 定制排序
/*List<String> s = Arrays.asList("ccc","bbb","aaa","hhh");
s.stream().sorted().forEach((e)->{
System.out.println(e);
});*/
employees.stream()
.sorted((e1,e2)->{
return e1.getAge()-e2.getAge();
}).forEach(e-> System.out.println(e));
}
6.终止操作-查找与匹配
allMatch 检查是否匹配所有元素
anyMatch 检查是否至少匹配一个元素
noneMatch 检查是否没有匹配所有元素
findFirst 返回第一个元素
findAny 返回当前流中的任意元素
count 返回流中元素的总个数
max 返回流中最大值
**min ** - 返回流中最小值
3.方法引用
什么是方法引用
若 Lambda体中的内容有方法已经实现了,我们可以使用方法引用(可以理解为方法引用是 Lambda表达式的另外一种表现形式)
主要有三种语法格式:
对象::实例方法名
public static void main(String[] args) {
/*void accept(String t); public void println(String x)
使用方法引用需要保证两个方法的返回值类型一致 且 参数类型要一致
*/
//Consumer<String> consumer = (x)-> System.out.println(x);
//对象::实例方法名
/*PrintStream ps = System.out;
Consumer<String> consumer = ps::println;
consumer.accept("hello world!!");*/
Employee e = new Employee("张三丰",100,20000);
// String get();
//Supplier<String> supplier = ()->{return e.getName();};
Supplier<String> supplier = e::getName;
String name = supplier.get();
System.out.println(name);
}
类::静态方法名
public static void main(String[] args) {
//Comparator<Integer> comparator = (x,y)->Integer.compare(x,y);
//类::静态方法名
Comparator<Integer> comparator = Integer::compare;
int result = comparator.compare(20, 10);
System.out.println(result);
}
类::实例方法名
public static void main(String[] args) {
//比较两个字符串是否相同
BiPredicate<String,String> biPredicate1 = (x,y)->x.equals(y);
//类::实例方法名
/*
* 什么时候可以使用这种方式:
* 传递的两个参数,第一个参数必须是方法的调用者,第二个参数是方法的参数
* */
BiPredicate<String,String> biPredicate2 = String::equals;
boolean test = biPredicate2.test("hello", "hello");
System.out.println(test);
}