函数接口
什么是函数式接口?
1、接口中有且仅有一个抽象方法。可以使用@FunctionInterface来标注此接口。
dk8以后接口中新增:
interface 的设计初衷是面向抽象,提高扩展性。可是我们对Interface 修改的时候,实现它的类也必须跟着改。 为了解决接口的修改与现有的实现不兼容的问题。
新 interface 的方法可以用default 或 static修饰,这样就可以有方法体,实现类也不必重写此方法。 一个 interface 中可以有多个方法被它们修饰,这 2 个修饰符的区别主要也是普通方法和静态方法的区别。
default修饰的方法,是普通实例方法,可以用this调用,可以被子类继承、重写。
static修饰的方法,使用上和一般类静态方法一样。但它不能被子类继承,只能用Interface调用。
(1) 默认方法。可以被子类重写,而可以通过对象调用该方法。
(2) 静态方法。不可以被子类重写,而且只能通过接口名调用。
系统提供了一些常见的函数式接口。
Consumer: 消费型函数式接口。 有参无返回值。
Function: 函数型函数式接口。 有参有返回值
Predicate: 断言型函数式接口。有参数返回值为boolean.
Supplier: 供给型函数接口。 无参有返回值。
1.1 Consumer
有参无返回值接口 Consumer接口是用来消费数据的,使用的时候需要指定一个泛型来定义参数类型
@FunctionalInterface
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
}
使用:将输入的数据统一转换为小写输出
public class ConsumerTest {
public static void main(String[] args) {
test(msg -> {
System.out.println(msg + "-> 转换为小写:" + msg.toLowerCase());
});
}
public static void test(Consumer<String> consumer){
consumer.accept("Hello World");
}
}
默认方法:andThen
如果一个方法的参数和返回值全部是Consumer类型,那么就可以实现效果,消费一个数据的时候,首先做一个操作,然后再做一个操作,实现组合,而这个方法就是Consumer接口中的default方法 andThen方法
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
具体的操作
public class ConsumerAndThenTest {
public static void main(String[] args) {
test2(msg1->{
System.out.println(msg1 + "-> 转换为小写:" + msg1.toLowerCase());
},msg2->{
System.out.println(msg2 + "-> 转换为大写:" + msg2.toUpperCase());
});
}
public static void test2(Consumer<String> c1,Consumer<String> c2){
String str = "Hello World";
//c1.accept(str); // 转小写
//c2.accept(str); // 转大写
//c1.andThen(c2).accept(str);
c2.andThen(c1).accept(str);
}
}
1.2 Function
有参有返回值的接口,Function接口是根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。有参数有返回值。
@FunctionalInterface
public interface Function<T, R> {
/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);
}
使用:传递进入一个字符串返回一个数字
public class FunctionTest {
public static void main(String[] args) {
test(msg ->{
return Integer.parseInt(msg);
});
}
public static void test(Function<String,Integer> function){
Integer apply = function.apply("666");
System.out.println("apply = " + apply);
}
}
默认方法:andThen,也是用来进行组合操作,
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
public class FunctionAndThenTest {
public static void main(String[] args) {
test(msg ->{
return Integer.parseInt(msg);
},msg2->{
return msg2 * 10;
});
}
public static void test(Function<String,Integer> f1,Function<Integer,Integer> f2){
/*Integer i1 = f1.apply("666");
Integer i2 = f2.apply(i1);*/
Integer i2 = f1.andThen(f2).apply("666");
System.out.println("i2:" + i2);
}
}
默认的compose方法的作用顺序和andThen方法刚好相反
而静态方法identity则是,输入什么参数就返回什么参数
1.3 Predicate
有参且返回值为Boolean的接口
@FunctionalInterface
public interface Predicate<T> {
/**
* Evaluates this predicate on the given argument.
*
* @param t the input argument
* @return {@code true} if the input argument matches the predicate,
* otherwise {@code false}
*/
boolean test(T t);
}
使用:
public class PredicateTest {
public static void main(String[] args) {
test(msg -> {
return msg.length() > 3;
},"HelloWorld");
}
private static void test(Predicate<String> predicate,String msg){
boolean b = predicate.test(msg);
System.out.println("b:" + b);
}
}
在Predicate中的默认方法提供了逻辑关系操作 and or negate isEquals方法
package com.ykq.jdk.fun;
import java.util.function.Predicate;
public class PredicateDefaultTest {
public static void main(String[] args) {
test(msg1 -> {
return msg1.contains("H");
},msg2 -> {
return msg2.contains("W");
});
}
private static void test(Predicate<String> p1,Predicate<String> p2){
/*boolean b1 = predicate.test(msg);
boolean b2 = predicate.test("Hello");*/
// b1 包含H b2 包含W
// p1 包含H 同时 p2 包含W
boolean bb1 = p1.and(p2).test("Hello");
// p1 包含H 或者 p2 包含W
boolean bb2 = p1.or(p2).test("Hello");
// p1 不包含H
boolean bb3 = p1.negate().test("Hello");
System.out.println(bb1); // FALSE
System.out.println(bb2); // TRUE
System.out.println(bb3); // FALSE
}
}
1.4 Supplier
无参有返回值的接口,对于的Lambda表达式需要提供一个返回数据的类型。
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
使用:
/**
* Supplier 函数式接口的使用
*/
public class SupplierTest {
public static void main(String[] args) {
fun1(()->{
int arr[] = {22,33,55,66,44,99,10};
// 计算出数组中的最大值
Arrays.sort(arr);
return arr[arr.length-1];
});
}
private static void fun1(Supplier<Integer> supplier){
// get() 是一个无参的有返回值的 抽象方法
Integer max = supplier.get();
System.out.println("max = " + max);
}
}
方法引用。
我们用Lambda表达式来实现匿名方法。但有些情况下,我们用Lambda表达式仅仅是调用一些已经存在的方法,除了调用方法外,没有其他任何多余的动作,在这种情况下,我们倾向于通过方法名来调用它,而Lambda表达式可以帮助我们实现这一要求,它使得Lambda在调用那些已经拥有方法名的方法的代码更简洁、更容易理解。方法引用可以理解为Lambda表达式的另外一种表现形式。
import java.util.function.Consumer;
import java.util.function.Supplier;
public class Test03 {
public static void main(String[] args) {
// Consumer<Integer[]> consumer= arr->{
// Test03.sum(arr);
// };
Consumer<Integer[]> consumer = Test03::sum;//::表示方法引用的符号。因为再lambda表达式中只有一条方法调用的语句。
Integer[] arr = {1, 2, 3, 4};
fun(consumer, arr);
}
//把lambda表达式中的内容抽取为一个公共方法。
public static void sum(Integer[] arr) {
int sum = 0;
for (int i = 0; i < arr.length; i++) {
sum += arr[i];
}
System.out.println("和为:" + sum);
}
public static void fun(Consumer<Integer[]> consumer, Integer[] arr) {
consumer.accept(arr);
}
}
2.1 静态方法引用。
只要lambda表达式符合 (参数)->类名.静态方法(参数)
使用方法引用的方式: 类名::静态方法。
例子: 对整形数组排序。
import java.util.Arrays;
import java.util.Comparator;
public class Test {
public static void main(String[] args) {
Integer[] arr={23,345,6,77,7,33,22};
//lambda表达式中有且仅有一条语句,方法的调用。
// Comparator<Integer> comparator=(o1,o2)->Integer.compare(o2,o1);
Comparator<Integer> comparator=Integer::compare;
Arrays.sort(arr,comparator);
System.out.println(Arrays.toString(arr));
}
}
2.2 实例方法引用
与静态方法引用的唯一区别: 使用对象调用的实例方法。
例子: 创建一个Student类对象,返回该学生的姓名;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.function.Function;
import java.util.function.Supplier;
public class Test{
public static void main(String[] args) {
Student stu=new Student("张三",18);
Supplier<String> supplier=stu::getName;lambda表达式中有且只有一条方法调用的语句。
fun(supplier);
}
//返回学生的姓名
public static void fun(Supplier<String> supplier){
String s = supplier.get();
System.out.println("该学生的姓名:"+s);
}
}
@AllArgsConstructor
@NoArgsConstructor
@Data
class Student{
private String name;
private int age;
}
2.3 对象方法引用
若Lambda参数列表中的第一个参数是实例方法的参数调用者,而第二个参数是实例方法的参数时,可以使用对象方法引用。
- 比较两个字符串是否相同;
public static void main(String[] args) {
// BiPredicate<String,String> biPredicate=(s1,s2)->{return s1.equals(s2);};
BiPredicate<String,String> biPredicate=String::equals;
fun2(biPredicate,"hello","world");
}
//判断两个字符串是否相同。
public static void fun2(BiPredicate<String,String> biPredicate,String str1,String str2){
System.out.println(biPredicate.test(str1, str2));
}
2.4 构造方法引用
注意:需要调用的构造器的参数列表要与函数式接口中抽象方法的参数列表保持一致。
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
public class Test07 {
public static void main(String[] args) {
Supplier<Teacher> supplier=()->new Teacher();
System.out.println("使用lambda表达式:"+supplier.get());
Supplier<Teacher> supplier2=Teacher::new; //必须构造方法的参数和函数式方法的参数要匹配
System.out.println("使用方法引用:"+supplier2.get());
Function<String,Teacher> function=name->new Teacher(name);
System.out.println("使用lambda表达式调用一个参数的构造方法:"+function.apply("张三"));
Function<String,Teacher> function2=Teacher::new;
System.out.println("使用方法引用调用一个参数的构造方法:"+function2.apply("李四"));
//Binary
BiFunction<String,Integer,Teacher> function1=Teacher::new;
System.out.println(function1.apply("张三",55));
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
class Teacher{
private String name;
private int age;
public Teacher(String name) {
this.name = name;
}
}