JDK1.8特性(上):函数式接口
Posted 码农里的蒸汽斯基
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDK1.8特性(上):函数式接口相关的知识,希望对你有一定的参考价值。
晚来天欲雪,能饮一杯无?--李商隐
相信有的小伙伴面试中经常会问到JDK1.8的新特性有哪些(虽然现在喝咖啡的那个老头已经都把13上线了),最近在网上找了一些资料总结了一下,希望能给对这方面有需要的攻城狮们一些帮助.
01
函数式接口的简单使用
函数式接口:有且只有一个抽象方法的接口,称之为函数式接口,当然,接口中还可以包括其他的方法,比如默认方法,静态方法,私有方法,定义函数式接口可以用注解@FunctionalInterface,这样程序可以检测当前接口是否是一个函数式接口.函数式接口的使用一般是作为方法的参数和返回值
话不多说,直接上代码
先定义一个函数式接口:
@FunctionalInterface
public interface MyDemo {
/**
* 定义一个抽象方法
*/
public abstract void myMethod();
}
然后写一个Demo和主方法
public class Demo {
/**
* 定义一个方法,参数使用函数式接口MyDemo
*/
public static void show(MyDemo demo) {
demo.myMethod();
}
public static void main(String[] args) {
//方式一:调用show方法,方法的参数是一个接口,所以可以传递接口的匿名内部类
show(new MyDemo() {
@Override
public void myMethod() {
System.out.println("使用匿名内部类的方式...");
}
});
//方式二:调用show方法,方法的参数是一个接口,可以使用lambda表达式
show(() -> {
System.out.println("使用lambda表达式的方式...");
});
//方式二可以简化为:
show(() -> System.out.println("使用lambda表达式的方式..."));
}
}
当然也可以直接写一个实现该接口的类,然后以该实现类为参数
这里使用内部类的话,会额外多加载一个class文件,但是lambda没有class的概念,所以会少加载一个class文件,对程序有一定的性能提升.
02
Lambda的延时加载
有些场景的代码执行后,结果不一定会被使用,从而造成性能浪费,而所有的lambda表达式是延迟执行的,即调用的时候,这正好可以作为解决方案,提升性能.
比如:
/**
* 编写一个打招呼的方法
* @param type 人员类型
* @param msg 问候语
*/
private static String greet(int type, String msg) {
if (1 == type) {
return msg;
}else{
return "告辞!";
}
}
public static void main(String[] args) {
String msg1 = "今天";
String msg2 = "天气";
String msg3 = "真好";
String greet1 = greet(1, msg1 + msg2 + msg3);
System.out.println(greet1);
}
这里就涉及到一些浪费性能的问题,调用方法,传递的第二个参数是一个拼接后的字符串,程序会先把字符串拼接好,然后再调用方法,问题来了,如果传递的人员类型不是1,那么就不会使用如此拼接后的字符串,所以感觉字符串就白拼接了,存在了浪费
使用lambda优化:
首先创建一个函数式接口:
@FunctionalInterface
public interface MsgBuilder {
//定义一个拼接消息的抽象方法,返回被拼接的消息
public abstract String builderMsg();
}
然后编写demo:
/**
* 编写一个打招呼的方法,参数传递人员类型和MsgBuilder接口
* @param type 人员类型
* @param mb 函数式接口
*/
private static String greet1(int type, MsgBuilder mb) {
//对人员类型进行判断,如果是1,则调用MsgBuilder接口中的builderMsg方法
if (1 == type) {
return mb.builderMsg();
}else{
return "告辞!";
}
}
测试:
public static void main(String[] args) {
String msg1 = "今天";
String msg2 = "天气";
String msg3 = "真好";
//调用打招呼方法,参数是一个函数式接口,所以可以传递一个lambda表达式
String greet2 = greet1(1, () -> {
return msg1 + msg2 + msg3;
});
//简化
String greet3 = greet1(1, () -> msg1 + msg2 + msg3);
System.out.println(greet2);
}
这样用lambda表达式作为参数传递,只有满足条件时,人员类型是1时,才会调用接口中的拼接方法,如果条件不满足,那么就不会拼接,就不会造成性能浪费
上述代码中会发现有的时候lambda表达式还会进一步简化,作者总结了一些简化的规律供大家参考:
简化规律
当只有一行代码的时候,如果有返回值,那么关键字return,大括号("{}")还有分号(";")都可以省略,有参数的话参数类型也可以省略
03
常用的函数式接口
在JDK1.8中,函数式接口都在java.util.function包里面,下面讲几种常见的函数式接口.
Supplier接口
Supplier接口是一个生产型接口,里面仅包含一个无参的方法, T get(),用来获取指定类型的对象数据.指定接口的泛型是什么类型,接口中get方法就会生产什么类型的数据
代码:
public class SupplierDemo {
public static String getName(Supplier<String> sup){
return sup.get();
}
public static void main(String[] args) {
String name = getName(() -> {
return "Krupp";
});
//上述可简化为:
String name1 = getName(()-> "Krupp");
System.out.println(name+"是一位歌手");
System.out.println(name1+"是一位歌手");
}
}
Consumer接口
Comsumer接口是一个消费型接口,泛型指定什么类型,就可以使用其内的accept方法消费什么类型的数据,关于怎么消费,可以自定义逻辑
代码:
import java.util.function.Consumer;
public class ConsumerDemo {
public static void consumerDemo(String name , Consumer<String> con){
con.accept(name);
}
public static void main(String[] args) {
consumerDemo("赵本山",(name)-> System.out.println(name+"是一位小品演员"));
}
}
里面还有一个默认方法:andThen()
andThen()方法:其作用是可以链接两个Consumer接口,把两个接口链接到一起,再对数据进行消费
点进去看一下JDK源码,相关源码如下:
/**
* 先判断第二个是否非空如果不是的话,就先执行当前对象的accept方法,然后,再执行第二个的accept方法
*/
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
/**
* requireNonNull方法只是用来判断非空的
*/
public static <T> T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}
很好理解啦,对吧!!,
下面是demo:
import java.util.function.Consumer;
public class ConsumerDemo {
/*需求:给定一个字符串,先将字符串进行大写,然后再小写
*
* */
public static void consumerDemo(String name , Consumer<String> con1,Consumer<String> con2){
//con1.accept(name);
//con2.accept(name);
con1.andThen(con2).accept(name);//和上面两行代码时相等的,谁先执行,谁就放前面
}
public static void main(String[] args) {
consumerDemo("HelloWorld",
(name)->{
//先大写
System.out.println(name.toUpperCase());
},
(name)->{
//再小写
System.out.println(name.toLowerCase());
});
}
}
Predicate接口
Predicate接口用于对某种数据类型的数据进行判断,其里面包含抽象方法test,接收一个参数,返回一个boolean值,符合条件则为true,不符合则返回false
代码如下:
public static boolean check(String s, Predicate<String> pre){
return pre.test(s);
}
/**
* 调用
*/
public static void main(String[] args) {
boolean b = check("Hello", (s) -> s.length() == 5);
System.out.println(b);
}
里面有几个常用来判断的默认方法,and(),or(),negate(),相当于java中的逻辑运算符'&&','||','!'
使用方式如下:
public static boolean check(String s, Predicate<String> pre1){
//return pre1.and(pre2).test(s); // return pre1.test(s) && pre2.test(s);
//return pre1.or(pre2).test(s); // return pre1.test(s) || pre2.test(s);
return pre1.negate().test(s); // return !pre1.test(s)
}
/**
* 调用
*/
public static void main(String[] args) {
boolean b = check("Hello", (s) -> s.length() == 5);
System.out.println(b);
}
Function接口
Function接口用来根据一个类型的数据得到另一个类型的数据,前者称之为前置条件,后者称为后置条件,里面的抽象方法 R apply(T t),根据T类型的参数获取类型R,至于如何获取,需要自定义逻辑
代码如下:
public static Integer changed(String str , Function<String,Integer> fun){
Integer in = fun.apply(str);
return in;
}
public static void main(String[] args) {
String str = "123";
changed(str,(s)-> Integer.valueOf(s));
}
其里面还有一个默认方法:andThen(),用来进行组合操作,使用方法与上面的Consumer里面的andThen()方法一样,这里就不再赘述
如有错误欢迎指正
扫码查看更多内容
以上是关于JDK1.8特性(上):函数式接口的主要内容,如果未能解决你的问题,请参考以下文章