一、函数式接口
在jdk8中什么是函数式接口:
- 被@FunctionalInterface注解修饰的。
- 接口里边只有一个非default的方法。
满足以上2个条件的即为函数式接口,ps:即使一个接口没有@FunctionalInterface修饰,但是满足2,那么这样的接口也会是函数式接口。
二、函数式接口的特点
- 接口有且仅有一个抽象方法,如上图的抽象方法compare
- 允许定义静态非抽象方法
- 允许定义默认defalut非抽象方法(default方法也是java8才有的,见下文)
- 允许java.lang.Object中的public方法,如上图的方法equals。
- FunctionInterface注解不是必须的,如果一个接口符合"函数式接口"定义,那么加不加该注解都没有影响。加上该注解能够更好地让编译器进行检查。如果编写的不是函数式接口,但是加上了@FunctionInterface,那么编译器会报错
情况一
- 接口有且仅有一个抽象方法,且加上了@FunctionInterface注解
@FunctionalInterface
public interface test_lamda {
void show();
}
public class test {
public static void main(String[] args) {
test_lamda test_lamda=()->{
System.out.println("test");
};
test_lamda.show();
}
}
情况二
- 接口有且仅有一个抽象方法,且没有加上了@FunctionInterface注解
public interface test_lamda {
void show();
}
public class test {
public static void main(String[] args) {
test_lamda test_lamda=()->{
System.out.println("test");
};
test_lamda.show();
}
}
情况三
- 使用java.lang.Object中的public方法
public interface test_lamda {
void show();
boolean equals(Object obj);
}
public class test {
public static void main(String[] args) {
test_lamda test_lamda=()->{
System.out.println("test");
};
test_lamda.show();
}
}
情况四
定义静态抽象方法,定义default
public interface test_lamda {
void show();
static void test1() {
System.out.println("test");
}
default void test2() {
System.out.println("test");
}
}
public class test {
public static void main(String[] args) {
test_lamda test_lamda=()->{
System.out.println("test");
};
test_lamda.show();
}
}
情况五
定义两个抽象方法
public interface test_lamda {
void show();
void test();
}
三、Lamda表达式
1.背景
Java8的最大变化是引入了Lambda表达式,它是所有Java8特性内容的基础——一种紧凑的,传递行为的方式。lambda表达式允许你通过表达式来代替功能接口,lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体, Lambda 表达式(Lambda expression)可以看作是一个匿名函数,也称为闭包
2.Lambda表达式语法
基本语法: (parameters) -> expression 或 (parameters) ->{ statements; }
Lambda表达式由三部分组成:
- parameters:参数,类似方法中的形参列表,这里的参数是函数式接口里的参数
- ->:可理解为“被用于”的意思
- 方法体:可以是表达式也可以代码块,是函数式接口里方法的实现。代码块可返回一个值或者什么都不反回,这里的代码块等同于方法的方法体。如果是表达式,也可以返回一个值或者什么都不返回
3.Lambda表达式五种不同的形式
//1、无需要参数,使用空括号()表示没有参数。
//该Lambda表达式实现了Runnable接口,该接口也只有一个run方法,没有参数,返回值类型为void
Runnable noArguments = () -> System.out.println("Hello World")
// 2. 接收一个参数,可以省略括号,返回其2倍的值
x -> 2 * x
//3. Lambda表达式主体不仅可以是表达式,也可以是一段代码块,使用大括号{}将代码括起来。
() ->{
System.out.println("Hello");
System.out.println("World")
}
//4.Lambda表达式可以包含多个参数的方法。接受2个参数(数字),并返回他们的和
(x,y)->x+y;
//5.声明是参数类型,也可以是多个参数。接收2个int型整数,返回他们的和
(int x,int y)->x+y;
四、常用的函数式接口
首先我们要知道的是lamda表达式的引入是为了什么,是为了解决经常new对象的问题,解决使用匿名内部类的问题而产生的所以我们先看一下两者在使用上的区别
public class test {
public static void main(String[] args) {
//匿名内部类的方法
startThread(new Runnable() {
@Override
public void run() {
System.out.println("线程启动了" + Thread.currentThread().getName());
}
});
startThread(()->{
System.out.println("线程启动"+Thread.currentThread().getName());
});
}
public static void startThread(Runnable r){
Thread thread = new Thread(r);
thread.start();
}
}
从上面的代码上我们可以看出使用lamda表达式写出了的代码要比使用匿名内部类的方法便捷很多
Java8在java.util.function包下预定义了大量的函数数式接口供我们使用。
- Supplier接口
- Consumer接口
- Predicate接口
- Function接口
4.1、Supplier接口
Supplier:包含一个无参的方法
- T get():获得结果
- 该方法不需要参数,它会按照某种实现逻辑(由Lambda表达式实现)返回一个数据。
- Supplier接口也称为生产型接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据供我们使用。
示例一
import java.util.function.Supplier;
public class test {
public static void main(String[] args) {
Supplier<String> supplier=()->{return "ltx";};
String s = supplier.get();
Supplier<Integer> supplier1=()->{return 1;};
Integer integer = supplier1.get();
System.out.println(s);
System.out.println(integer);
};
}
示例二
import java.util.function.Supplier;
public class test {
public static void main(String[] args) {
int []arr={1,2,3,4,5,6};
int maxvalue=getMax(()->{
int a=arr[0];
for (int i = 0; i < arr.length; i++) {
if(a<arr[i]){
a=arr[i];
}
}
return a;
});
System.out.println(maxvalue);
};
public static Integer getMax(Supplier<Integer> s){
return s.get();
}
}
4.2、Consumer接口
Consumer:包含两个方法
- void accept(T t):对给定的参数执行此操作。
- default Consumer andThen(Consumer after):返回一个组合的Consumer,依次执行操作,然后执行after操作。
- Consumer接口也称为消费型接口,它消费的数据的数据类型由泛型指定。
示例一:
import java.util.function.Consumer;
public class test {
public static void main(String[] args) {
Consumer<Integer> integerConsumer = System.out::println;
integerConsumer.accept(1);
Consumer<String> stringConsumer = name -> System.out.println(name);
stringConsumer.accept("ltx");
}
}
示例二:
package com.test10;
import java.util.function.Consumer;
public class ConsumerTest {
public static void main(String[] args) {
String[] arr={"唐青枫,20","曲无忆,21","离玉堂,22","叶知秋,23"};
printInfo(arr,
(String str)->{
String name=str.split(",")[0];
System.out.print("姓名:"+name);
},
(String str)->{
int age=Integer.parseInt(str.split(",")[1]);
System.out.println(",年龄:"+age);
});
}
private static void printInfo(String[] arr, Consumer<String> con1, Consumer<String> con2){
for(int i=0;i<arr.length;i++){
con1.andThen(con2).accept(arr[i]);
}
}
}
4.3、Predicate接口
Predicate:常用的四个方法:
- boolean test(T t):对给定的参数进行判断(判断逻辑由Lambda表达式实现),返回一个布尔值。
- default Predicate negate():返回一个逻辑的否定,对应逻辑非。
- default Predicate and(Predicate other):返回一个组合判断,对应短路与。
- default Predicate or(Predicate other):返回一个组合判断,对应短路或。
- Predicate接口通常用于判断参数是否满足指定的条件。
package com.test12;
import java.util.function.Predicate;
public class Demo {
public static void main(String[] args) {
//Lambda表达式
// boolean b=checkString("Hello",(String str)->{
// return str.length()>8;
// });
boolean b=checkString("Hello",str->str.length()>8);
System.out.println(b);
System.out.println("****************************");
b=checkString1("Hello",str->str.length()>8);
System.out.println(b);
}
private static boolean checkString(String str, Predicate<String> pre){
return pre.test(str);
}
private static boolean checkString1(String str,Predicate<String> pre){
// return !pre.test(str);
//上一句等价于
return pre.negate().test(str);
}
}
4.4、Function接口
Function:常用的两个方法
- R apply(T t):将此函数应用于给定的参数。
- default Function andThen(Function after):返回一个组合函数,首先将该函数应用于输入,然后将after函数应用于结果。
- Function:接口通常用于对参数进行处理,转换(处理逻辑由Lambda表达式实现),然后返回一个新的值。
示例
import java.util.function.Function;
public class test {
public static void main(String[] args) {
convert("666", Integer::parseInt);
convert(666, String::valueOf);
convert("666", Integer::parseInt, String::valueOf);
}
private static void convert(String s, Function<String,Integer> fun){
int i=fun.apply(s);
System.out.println(i);
}
private static void convert(Integer i,Function<Integer,String> fun){
String s=fun.apply(i);
System.out.println(s);
}
private static void convert(String s,Function<String,Integer> fun1,Function<Integer,String> fun2){
// int i=fun1.apply(s);
// String str=fun2.apply(i);
String str=fun1.andThen(fun2).apply(s);
System.out.println(str);
}
}