函数式接口-Java 8 新特性
函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口,可以指定 Object 定义的任何公有方法。。
函数式接口可以被隐式转换为 lambda 表达式。
Lambda 表达式和方法引用(实际上也可认为是Lambda表达式)上。
@FunctionalInterface
public interface IFuntionSum<T extends Number> {
T sum(List<T> numbers); // 抽象方法
}
@FunctionalInterface
public interface IFunctionMulti<T extends Number> {
void multi(List<T> numbers); // 抽象方法
boolean equals(Object obj); // Object中的方法
}
如果改为以下形式,则不是函数式接口:
@FunctionalInterface
public interface IFunctionMulti<T extends Number> extends IFuntionSum<T> {
void multi(List<T> numbers);
@Override
boolean equals(Object obj);
}
// IFunctionMulti 接口继承了 IFuntionSum 接口,此时 IFunctionMulti 包含了2个抽象方法
说明:
1: 可以用
@FunctionalInterface
标识函数式接口,非强制要求,但有助于编译器及时检查接口是否满足函数式接口定义
2: 在 Java 8 之前,接口的所有方法都是抽象方法,在 Java 8 中新增了接口的默认方法
JDK 1.8中java.util.function
包含很多类,支持函数式编程
序号 | 接口 & 描述 |
---|---|
1 | BiConsumer<T,U>代表了一个接受两个输入参数的操作,并且不返回任何结果 |
2 | BiFunction<T,U,R>代表了一个接受两个输入参数的方法,并且返回一个结果 |
3 | BinaryOperator< T >代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果 |
4 | BiPredicate<T,U>代表了一个两个参数的boolean值方法 |
5 | BooleanSupplier代表了boolean值结果的提供方 |
6 | Consumer< T > 代表了接受一个输入参数并且无返回的操作 |
很多不一一列举了。
函数式接口实例
参考:https://www.runoob.com/java/java8-functional-interfaces.html
public class UseFunctionInterface {
public static void main(String args[]){
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
// Predicate<Integer> predicate = n -> true
// n 是一个参数传递到 Predicate 接口的 test 方法
// n 如果存在则 test 方法返回 true
System.out.println("输出所有数据:");
// 传递参数 n
eval(list, n->true);
// Predicate<Integer> predicate1 = n -> n%2 == 0
// n 是一个参数传递到 Predicate 接口的 test 方法
// 如果 n%2 为 0 test 方法返回 true
System.out.println("输出所有偶数:");
eval(list, n-> n%2 == 0 );
// Predicate<Integer> predicate2 = n -> n > 3
// n 是一个参数传递到 Predicate 接口的 test 方法
// 如果 n 大于 3 test 方法返回 true
System.out.println("输出大于 3 的所有数字:");
eval(list, n-> n > 3 );
}
public static void eval(List<Integer> list, Predicate<Integer> predicate) {
for(Integer n: list) {
if(predicate.test(n)) {
System.out.println(n + " ");
}
}
}
}
控制台输出:
自定义函数式接口
//注解可加可不加,建议加上
@FunctionalInterface
public interface MyFunctionInterface {
void saySomething(String message);
}
实现类:
public class MyFunctionInterfaceImpl implements MyFunctionInterface{
@Override
public void saySomething(String message) {
System.out.println("重写了抽象方法 "+message);
}
}
测试类:
public static void show(MyFunctionInterface myInter) {
myInter.saySomething("");
}
public static void main(String[] args) {
//调用show方法,方法的参数是一个接口,可以传递接口的实现类对象
show(new MyFunctionInterfaceImpl());
//调用show方法,方法的参数是一个接口,可以传递接口的匿名内部类
show(new MyFunctionInterface() {
@Override
public void saySomething(String message) {
System.out.println("使用匿名内部类重写接口中的抽象方法");
}
});
//调用show方法,方法的参数是一个接口,可以使用Lambda表达式
show((s)->{
System.out.println("使用Lambda表达式重写接口中的抽象方法"+s);
});
//简化Lambda表达式
show((s)-> System.out.println("使用简化的Lambda表达式重写接口中的抽象方法"+s));
}
控制台输出:
函数式编程
这里模拟一个简单的不同用户登录发送不同的消息功能
public class demo1 {
public static void showMessage(int level, String message) {
//对用户类型判断,不同类型发送不同消息
if(level == 1) {
System.out.println("hello "+message);
}else {
System.out.println("hello admin");
}
}
public static void main(String[] args) {
//定义用户信息
String name = "周杰伦";
String position = "good!!!!!!!!";
showMessage(1,name+position);
}
}
代码存在的问题:无论 level 是否满足要求,作为 showMessage方法的第二个参数,三个字符串一定会首先被拼接并传入方法内,然后才会进行级别判断。如果级别不符合要求,那么字符串的拼接操作就白做了,存在性能浪费。
写一个函数式接口
@FunctionalInterface
public interface MessageBuilder {
String buildMessage();
}
public class demo2 {
public static void showMessage(int level, MessageBuilder mb) {
//对用户类型判断,不同类型发送不同消息
if(level == 1) {
System.out.println("hello "+mb.buildMessage());
}else {
System.out.println("hello admin");
}
}
public static void main(String[] args) {
//定义用户信息
String name = "周杰伦";
String position = "good!!!!!!!!";
showMessage(1,()->{
System.out.println("Lambda执行!");
return name+position;
});
}
}
使用 Lambda 表达式作为参数传递时,仅仅是把参数传递到showMessage方法中,只有满足1
的条件,才会调用接口MessageBuilder中的方法builderMessage,再进行字符串拼接,如果不满足,接口MessageBuilder中的方法就不会执行,拼接字符串的代码也不会执行,所以就不会存在性能上的浪费。
使用Lambda作为参数和返回值
public class demo3 {
public static void startThread(Runnable r) {
new Thread(r).start();
}
//runnable是一个函数式接口
public static void main(String[] args) {
startThread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"-->"+"线程启动了。");
}
});
//使用Lambda简化
startThread(()-> System.out.println(Thread.currentThread().getName()+"-->"+"线程启动了。"));
}
}
//方法的返回值类型是一个函数式接口,那么就可以直接返回一个Lambda表达式
public class demo4 {
public static Comparator<String> getComparator() {
// return new Comparator<String>() {
// @Override
// public int compare(String o1, String o2) {
// return o2.length()-o1.length();
// }
// };
//参数可以省略
// return (String o1, String o2)->{
// return o2.length()-o1.length();
// };
//简化
return (o1, o2) -> o2.length() - o1.length();
}
public static void main(String[] args) {
String[] arr = {"aaa", "b", "cccccc", "ddddddddd"};
System.out.println("排序前:" + Arrays.toString(arr));
Arrays.sort(arr, getComparator());
System.out.println("排序后:" + Arrays.toString(arr));
}
}
函数式接口
Supplier接口
public class demo5 {
public static String getString(Supplier<String> sup) {
return sup.get();
}
public static void main(String[] args) {
String s = getString(()->{
return "周杰伦";
});
System.out.println(s);
String s1 = getString(()->"完美主义");
System.out.println(s1);
}
}
Customer接口
抽象方法:accept
Consumer 接口中包含抽象方法 void accept(T t) ,意为消费一个指定泛型的数据。
public class demo6 {
public static void method(String name, Consumer<String> con) {
con.accept(name);
}
public static void main(String[] args) {
method("周杰伦出新专辑拉",(name)->{
//对传递的字符串进行消费
//消费方式:直接输出字符串
System.out.println(name);
//消费方式:反转字符串
String reName = new StringBuilder(name).reverse().toString();
System.out.println(reName);
});
}
}
默认方法:andThen
需要两个Consumer接口,可以把两个Consumer接口组合到一起,再对数据进行消费。
注意:谁写前面谁先消费
public class demo7 {
public static void method(String s, Consumer<String> con1, Consumer<String> con2) {
// con1.accept(s);
// con2.accept(s);
//先执行con1 再执行 con2
con1.andThen(con2).accept(s);
}
public static void main(String[] args) {
method("Hello", (t) -> {
//消费方式:把字符串转换为大写输出
System.out.println(t.toUpperCase()); //HELLO
}, (t) -> {
//消费方式,把字符串转换为小写输出
System.out.println(t.toLowerCase()); //hello
});
}
}
Predicate接口
需要对某种类型的数据进行判断,从而得到一个boolean值结果。
抽象方法:test
public class demo8 {
public static boolean checkString(String s, Predicate<String> pre) {
return pre.test(s);
}
public static void main(String[] args) {
String s = "ABCDE";
boolean b = checkString(s, (String str) -> {
//对参数传递的字符串进行自定义判断
return s.length() > 5;
});
System.out.println(b); //false
//优化Lambda
boolean b1 = checkString(s,str->s.length() > 4);
System.out.println(b1); //true
}
}
默认方法:and
表示并且关系,也可以用于连接两个判断条件,其JDK源码为:
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) ‐> test(t) && other.test(t);
}
public class demo9 {
public static boolean checkString(String s, Predicate<String> pre1, Predicate<String> pre2) {
// return pre1.test(s) && pre2.test(s);
return pre1.and(pre2).test(s);
}
public static void main(String[] args) {
String s = "abfsgsgs";
boolean b = checkString(s,(String str)->{
return str.length()>6;
},(String str)->{
return str.contains("a");
});
System.out.println(b); //true
}
}
默认方法:or
与 and 的“与”类似,默认方法 or 实现逻辑关系中的“或”。JDK源码为:
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) ‐> test(t) || other.test(t);
}
public static boolean checkString(String s, Predicate<String> pre1,Predicate<String> pre2) {
// return pre1.test(s) || pre2.test(s);
return pre1.or(pre2).test(s);
}
public static void main(String[] args) {
String s = "abfsg";
boolean b = checkString(s,(String str)->{
return str.length()>10;
},(String str)->{
return str.contains("a");
});
System.out.println(b); // true
}
默认方法:negate
取反
default Predicate<T> negate() {
return (t) ‐> !test(t);
}
public class demo11 {
public static boolean checkString(String s, Predicate<String> pre) {
return pre.negate().test(s);
}
public static void main(String[] args) {
String s = "abfsg";
boolean b = checkString(s,(String str)->{
return str.length()>10;
});
System.out.println(b); // true
}
}
Function接口
Function<T,R> 接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件, 后者称为后置条件。
抽象方法:apply
根据类型T的参数获取类型R的结果。
public class demo12 {
public static void change(String s, Function<String,Integer> fun) {
Integer in = fun.apply(s);
System.out.println(in);
}
public static void main(String[] args) {
String s = "1234";
change(s,(String str)->{
return Integer.parseInt(str); //1234
});
change(s,str->Integer.parseInt(str)); //1234
}
}
默认方法: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 demo13 {
public static void change(String s, Function<String,Integer> fun1, Function<Integer,String> fun2) {
String ss = fun1.andThen(fun2).apply(s);
System.out.println(ss);
}
public static void main(String[] args) {
String s = "123";
change(s,(String str)->{
return Integer.parseInt(s)+10;
},(Integer i)->{
return i+""; //133
});
//优化
change(s,str->Integer.parseInt(s)+10,i->i+""); //133
}
}