内置函数式接口的由来
我们知道使用Lambda表达式的前提是需要有函数式接口.而Lambda表达式使用时不关心接口名,抽象方法名,只关心抽象方法的参数列表和返回值类型.因此为了让我们使用Lambda方便,JDK提供了大量常用的函数式接口.
常用的内置函数式接口介绍
他们主要在java.util.function包中,下面是常用的几个接口
- Supplier接口 - 供给型接口,没有参数但是有返回值
Supplier 与 Function 不同,它不接受入参,直接为我们生产一个指定的结果,有点像生产者模式
@FunctionalInterface
public interface Supplier<T> {
T get();
}
供给型接口,通过Supplier接口中的get()方法可以得到一个值,无参有返回值的接口。
public class Demo3UseFunctionInterface {
public static void main(String[] args) {
test((int a, int b) -> {
System.out.println(a + b);
});
}
public static void test(Operation operation) {
operation.getSum(1, 3);
}
}
interface Operation {
public abstract void getSum(int a, int b);
}
使用Supplier接口作为方法参数类型,通过lambda表达式求出int数组中的最大值。
//使用lambda表达式返回数组最大元素
public static void main(String[] args) {
System.out.println("开始了");
printMax(()->{
System.out.println("步骤2");
int [] arr = {11,2,3,45,54,99};
Arrays.sort(arr);
return arr[arr.length-1];
});
}
private static void printMax(Supplier<Integer> supplier){
System.out.println("步骤1");
int max = supplier.get();
System.out.println("max:"+ max);
}
执行步骤:
-
先执行
main()开始了,输出开始了 -
执行
printMax(),输出步骤1 -
执行步骤
supplier.get()实际是执行printMax()里的实现方法,输出步骤23.最后执行main的剩余代码,输出99 -
Consumer接口 - 消费性接口 有参数但是没有返回值
对于 Consumer,我们需要提供入参,用来被消费
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
public static void main(String[] args) {
System.out.println("开始了");
printMax((String str)->{
System.out.println(str.toUpperCase());
});
}
private static void printMax(Consumer<String> consumer){
System.out.println("aa");
consumer.accept("hello world");
}
默认方法: andThen
如果一个方法的参数和返回值全都是comsumer类型,那么就可以实现效果:消费一个数据的时候,首先做一个操作,然后在做一个操作,实现组合。而这个方法就是comsumer接口中的default方法andThen
public static void main(String[] args) {
System.out.println("开始了");
printMax((String str)->{
System.out.println(str.toLowerCase());
},(String str)->{
System.out.println(str.toUpperCase());
});
}
private static void printMax(Consumer<String> consumer1,Consumer<String> consumer2){
System.out.println("aa");
String str = "Hello World";
// consumer1.accept(str);
// consumer2.accept(str);
consumer1.andThen(consumer2).accept(str);
}
- Function 接口
Function 函数式接口的作用是,我们可以为其提供一个原料,他给生产一个最终的产品。通过它提供的默认方法,组合,链行处理(compose, andThen):
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
public static void main(String[] args) {
System.out.println("开始了");
getNum((String str) -> {
return Integer.parseInt(str);
}, (Integer integer) -> {
return integer * 5;
});
}
private static void getNum(Function<String, Integer> function1, Function<Integer, Integer> function2) {
System.out.println("aa");
Integer num1 = function1.andThen(function2).apply("20");
// Integer num2 = function2.apply(num1);
System.out.println("num1= " + num1);
}
- Predicate断言
Predicate 是一个可以指定入参类型,并返回 boolean 值的函数式接口。它内部提供了一些带有默认实现的方法,可以 被用来组合一个复杂的逻辑判断(and, or, negate)
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
public static void main(String[] args) {
idLongName((String name) ->{
return name.length()>3;
});
}
public static void idLongName (Predicate<String> predicate){
System.out.println("aa");
boolean isLong = predicate.test("迪丽热巴");
System.out.println(isLong);
}
方法引用
方法引用的格式
符号表示: ::
符号说明: 双冒号为方法引用运算符,而他所在的表达式被称为方法引用
应用场景: 如果Lambda所要实现的方案,已经有其它方法存在的相同方案,那么可以使用方法引用
常用引用方式
方法引用在JDK8中使用方式相当灵活,有一下几种形式
- instanceName :: methodName 对象::方法名
- ClassName :: staticMethodName 类名:: 静态方法
- ClassName :: methodName 类名:: 普通方法
- ClassName :: new 类名 :: new 调用构造器
- TypeName [] :: new String ::new 调用数组的构造器
对象名 :: 引用成员方法
public void test01(){
Date now = new Date();
Supplier<Long> su1 = ()->{
return now.getTime();
};
Supplier<Long> su2 =now:: getTime;
Long aLong = su1.get();
System.out.println("aLong="+aLong);
}
方法引用有2个注意事项
- 被引用的方法,参数要和接口抽象方法的参数一样
- 当接口抽象方法有返回值时,被引用的方法也必须有返回值
类名 :: 引用静态方法
public void test02(){
Supplier<Long> su1 = ()->{
return System.currentTimeMillis();
};
Long aLong = su1.get();
System.out.println("aLong="+aLong);
Supplier<Long> su2 =System::currentTimeMillis;
Long aLong2 = su2.get();
System.out.println("aLong2="+aLong2);
}
类名 :: 实例方法
public static void test03(){
Function<String,Integer> su1 = (String str)->{
return str.length();
};
Integer count = su1.apply("hello");
System.out.println("count="+count);
//类名:: 实例方法(注意: 类名::实例方法 实例方法实际上会将第一个参数作为方法的调用者)
Function<String,Integer> su2 =String::length;
Integer count2 = su2.apply("hello");
System.out.println("count2="+count2);
//类名:: 实例方法(注意: 类名::实例方法 实例方法实际上会将第一个参数作为方法的调用者)
//BiFunction<String,Integer,String> bif2 =String::substring;
BiFunction<String,Integer,String> bif2 = (String str,Integer index) ->{
return str.substring(index);
};
String str2= bif2.apply("helloword",3);
System.out.println("str2="+str2);
}
类名:: new 引用类的构造器
public static void test04(){
Supplier<Person> person = ()->{
return new Person();
};
// Supplier<Person> person = Person::new;
Person person1 = person.get();
System.out.println("person1="+person1);
BiFunction<String,Integer,Person> bif = (String name,Integer age) ->{
return new Person(name,age);
};
//BiFunction<String,Integer,Person> bif = Person:: new;
Person p2 = bif.apply("刘新", 29);
System.out.println("p2="+p2);
}
数组::new 引用数组构造器
数组也是object 的子类对象,所以同样具有构造器,只是语法稍有不同
public static void test05(){
Function<Integer,int [] > f1 = (Integer index)->{
return new int[index];
};
//Function<Integer,int [] > f1 = int[]::new;
int[] arr = f1.apply(10);
System.out.println("arr="+arr);
}