函数式接口
什么是函数式接口?
- 在java中'有且仅有一个抽象方法的接口',就称为函数式接口。
- 可以通过Lambda表达式来创建该接口的对象。(若Lambda表达式抛出一个受检异常,那么该异常需要在目标接口的抽象方法上进行声明)
- 我们可以在任意函数接口上
使用@Functionallnterface 注解
java8中,这样可以检查它是否是一个函数式接口,同时javadoc也会包含一条声明,说明这个接口是一个函数式接口。
- 备注:"语法糖"是指使用更加方便,但是原理不变的代码语法。例如在遍历集合时使用的for-each语法,其实底层的实现原理仍然是迭代器,这便是"语法糖"。从应用层面来讲,Java中的Lambda可以被当做是匿名内部类的"语法糖",但是二者在原理上是不同的。
- Lambda表达式的本质:作为函数式接口的实例
java.util.function
包下定义了java8丰富的函数接口
参考地址: https://www.cnblogs.com/dgwblog/p/11739500.html#2677150870
函数类格式:
- 只有确保接口当中有且仅有一个抽象方法
修饰符 interface 接口名称{ public abstract 返回值 方法名称(参数列表) // 其他方式 }// public abstract 可以不写 编译器自动加上修饰符 interface 接口名称{ 返回值 方法名称(参数列表) // 其他方式 }
自定义函数接口
@FunctionalInterface//注解表明他是一个函数接口interface Test { void method();}public class FunctionInterfaceDemo { public static void main(String[] args) { //第一种 Test t = () -> { System.out.println("zaide"); }; t.method(); //第二种 show(() -> System.out.println("你好")); } //定义一个方法含有接口的方法 public static void show(Test test) { //调用里面的方法 test.method(); }}
自定义函数接口的用途
对于自定义的函数式接口,一般用于方法的参数和返回值上。
函数式编程
- 能够兼顾java的面向对象特性基础上,通过Lambda表达式上与方法引用,为开发者打开函数式编程的大门。
Lambda的延迟加载
描述: 有些场景的代码运行执行后,结果不一定会被使用到,从而造成性能的浪费。而lambda表达式是延迟执行的,正好可以解决此问题,提升性能。
性能浪费的日志案例
注:日志可以帮助我们快速的定位问题,记录程序运行过程中的情况
/** * showLog()方法如果传递的日志等级不是3以下的 * 那么就不会显示拼接之后的信息 * 所以对于程序来说,你这个日志信息的拼接动作就浪费了 */public class LoggerDemo { //定义一个方法根据日志的等级,显示日志信息的内容 public static void showLog(int level, String message) { //对日志的等级进行判断,如果日志的等级在3以下,就全部输出 if (level <= 3) { System.out.println(message); return; } System.out.println("日志等级较高"); } public static void main(String[] args) { String message1 = "执行mysql"; String message2 = "执行java.exe"; String message3 = "执行tomcat"; showLog(2, message1 + message2 + message3); }}
使用Lambda表达式优化
/** * 使用lambda优化刚才的日志输出案例 * lambda的特点,具有延迟加载 * lambda使用前提;提供一个函数接口 */public class LoggerLambdaDemo { //定义一个显示日志的方法,方法的参数传日志的等级和BuildLogMassage接口 public static void showLog(int level, BuildLogMassage log) { //对于日志等级进行判断,如果小于3就输出 if (level <= 3) { //// 实际上利用内部类 延迟的原理,代码不相关 无需进入到启动代理执行 System.out.println(log.sendLogMassage()); } } /* * 使用lambda表达式作为参数传递, * 只有满足条件,日志的等级小于等于3 * 才会调用此接口BuildLogMassage种的方法 * 才会调用字符串的拼接 * 如果条件不满足,日志的等级大于3 * 那么BuildLogMassage接口种的方法与不会执行 * 所以拼接字符串的动作也不会执行 * 所以不会存在性能上的浪费。 **/ public static void main(String[] args) { String message1 = "执行mysql"; String message2 = "执行java.exe"; String message3 = "执行tomcat"; //调用showLog方法,参数是一个函数接口,可以使用lambda表达式 showLog(5, () -> { //返回一个拼接好的字符串 //System.out.println("前面的日志等级大于3此处不执行");//没有输出 return message1 + message2 + message3; }); }}@FunctionalInterfaceinterface BuildLogMassage { //定义有且只有一个抽象方法,拼接日志信息 String sendLogMassage();}//备注;实际上使用内部类也可以达到这样的操作,只是将代码操作延迟到另外一个对象当中通过调用方法来完成。后面的代码执行取决于前面的条件的判断的结果。
备注:SLF4J是应用非常广泛的日志框架,它在记录日志时为了解决这种性能浪费的问题,并不推荐首先进行字符串的拼接,而是将字符串的若干部分作为可变参数(包装为数组)传入方法中,仅在日志级别满足要求的情况下才会进行字符串拼接。
使用Lambda作为方法的参数和返回值
参数
在Java当中,lambda表达式是作为匿名内部类的替代品,如果一个方法的参数一个函数式接口类型,那么可以使用lambda表达式进行替代。java.lang.Runnable接口就是一个函数式接口
代码演示
public class Demo { //定义一个方法,开启线程 public static void startThread(Runnable r) { new Thread(r).start(); } public static void main(String[] args) { startThread(() -> { System.out.println("线程任务--》线程任务被执行了"); }); //优化 startThread(() -> System.out.println("线程任务--》线程任务被执行了") ); }}
返回值
如果一个方法的返回值类型是一个函数式接口,那么我们可以直接使用一个lambda表达式java.util.Comparator 接口是一个函数式接口
代码演示
public class Demo2 { //定义一个方法,方法的返回值类型是一个函数式接口类型Comparator public static Comparator<String> createCoparator() { //返回值就是一个函数式接口/* return new Comparator<String>() { @Override public int compare(String o1, String o2) { //自定义比较的规则 升序/降序 //字符串的长度 return o1.length() - o2.length(); } };*/ //使用lambda表达式使用 字符串长度升序/* return (o1, o2) -> o1.length() - o2.length();*/ return Comparator.comparingInt(String::length); } public static void main(String[] args) { String [] str={"ddd","aa","zz","qqqq","pppp"}; Arrays.sort(str,createCoparator()); System.out.println(Arrays.toString(str)); }}
常用的函数式接口
- 常用接口
- jdk提供了大量常用的函数式接口,丰富lambda表达式的使用场景。他们主要在java.uiil.function包下。
Supplier<T>(供应接口)
描述
java.util.function.Supplier 接口,该接口有且仅有一个无参的方法;
方法: T get(); 用来获取一个泛型参数指定类型的对象,由于这是一个函数接口,这就意味就可以使用Lambda表达式。需要对外提供一个符合泛型类型的对象数据。
Supplier<T>接口称为生产型接口,指定接口的泛型是什么类型,那么接口种的get()方法就会生产什么类型的数据。
public class SupplierDemo { //定义一个方法,方法的参数传递一个Supplier<T>接口 //泛型指定String,get方法就会返回一个String public static String getString(Supplier<String> sup) { return sup.get(); } //定义一个方法,方法的参数传递一个Supplier<T>接口 //泛型指定位Integer,get方法就会返回一个int public static Integer getNum(Supplier<Integer> sup) { return sup.get(); } public static void main(String[] args) { //调用getString方法,方法的参数传递supplier<T>是一个 //函数接口,那么我们就可以使用lambda/* String s = getString(() -> { return "你好java"; }); System.out.println(s);*/ //求一个int类型的数组种的最值 int[] arr = {10, 23, 4, 76, 98, 2}; Integer Max = getNum(() -> { //求出数组的最大值 int max = arr[0]; for (int i : arr) { //判断 if (max < i) { max = i; } } return max; }); //输出最大值 System.out.println(Max); }}
Consumer<T>消费型接口
描述
java.util.funciton.Consumer<T> 接口刚好和Supplier接口相反,他不是用来生产一个数据,而是消费一个数据。 数据的类型有泛型来指定。 void accept(T t); 其中方法accept;意思就是消费一个指定的类型的数据
代码实例
public class ConsumerDemo { //定义一个方法,方法的参数传递一个Consumer<T>接口,传递一个字符串 public static void Consumer(String str, Consumer<String> con) { con.accept(str); } public static void main(String[] args) { //来调用消费方法Consumer, //Consumer<String>接口是一个函数式接口类型,所以可以使用lambda Consumer("China", (name) -> {//name是将你传入的那个数据起个名字 //把里面的字符转换为大写 String s = name.toUpperCase();//转换大写 String str = new StringBuffer(s).reverse().toString();//反转 System.out.println(str); }); }}
Consumer的默认方法
'andThen' //于是呐/然后 如果一个方法的参数和返回值全都是Consumer类型,那么就可以实现这样的效果;'消费数据的时候,首先做一个消费的操作,在做一个消费的操作'。实现组合。可以通过Consumer接口总的默认方法;'andThen'来实现
- 代码如下
public class ConsumerAndThenDemo { //定义一个方法,方法参数传递一个字符串,和两个Consumer接口 //Consumer这个接口的泛指定为字符串 public static void consumer(String str, Consumer<String> con1, Consumer<String> con2) {/* con1.accept(str); con2.accept(str);*/ //andThen 连续消费 //先执行左边的Consumer--con1的动作,andThen-->再次执行Consumer--> con2动作 con1.andThen(con2).accept(str); //消费规则;con1连接con2,先执行con1消费数据,在执行con2消费数据 } public static void main(String[] args) { //由于consumer方法的参数Consumer接口是一个函数接口所以使用lambda consumer("java31-都是-大佬", (name1) -> { //消费规则 //截取传入的字符串 String s = name1.substring(0, 6); System.out.println(s); }, (name2) -> { //定义消费的规则,分成字符串数组展示 String[] strs = name2.split("-"); System.out.println(Arrays.toString(strs)); }); }}//通过查看源码的知,andThen方法不允许传入一个null对象否则就抛出一个空指针'想要把两次消费的动作连接起来,需要传入两个Consumer接口,通过andThen方法实现一步一步的执行消费动作'
练习;定义一个字符串数组,存储每一个人的信息如;张三,20,郑州市,存储5个人的信息,使用consumer接口,按照指定的格式打印输入;姓名;张三;年龄;20;地址;郑州市,要求打印姓名动作为第一个consumer接口的规则,将打印年龄的动作为第二个consumer接口的规则,将打印地址的动作为第三个consumer接口的规则。最终将三个consumer接口按照规定的顺序拼接出来。antThen
public class Test { public static void show(People[] people, Consumer<People> c1, Consumer<People> c2, Consumer<People> c3) { //循环遍历依此拿到个是一个人信息 for (People p : people) { //先拿到的先执行 c1.andThen(c2).andThen(c3).accept(p); } } public static void main(String[] args) { People[] p = {new People("古力娜扎", 20, "新疆"), new People("迪丽热巴", 25, "齐齐哈尔"), new People("马儿扎哈", 19, "内蒙")}; //调用方法使用lambda表达式进行使用 show(p, (c1) -> { System.out.print(c1.getName()+" "); }, (c2) -> { System.out.print(c2.getAge()+" "); } , (c3) -> { System.out.println(c3.getCity()+" "); }); }}//用一个类来表示class People { private String name; private int age; private String city; public People() {} @Override public String toString() { return "People{" + "name='" + name + '\'' + ", age=" + age + ", city='" + city + '\'' + '}'; } public String getName() {return name;} public void setName(String name) {this.name = name;} public int getAge() {return age;} public void setAge(int age) {this.age = age;} public String getCity() { return city;} public void setCity(String city) {this.city = city;} public People(String name, int age, String city) {this.name = name;this.age = age;this.city = city;}}
Predicate<T>
- 描述
java.util.Predicate 函数式接口。其中唯一的抽象方法。boolean test(T t);该方法返回布尔类型值,代表指定的条件满足返回true,那么Stream的方法filter将其中的元素保留下来,如果条件不满足返回false,那么filter方法会舍弃该元素。
Function<T, R>
描述
java.util.stream.Function 函数式接口。其中唯一的抽象方法:R apply(T t); (接口本身接口两个数据T/R,方法返回一个数据R)//可以将一种类型T类型的数据转换成R类型的数据,那么这种转换的动作,我们称之为"映射"
方法引用构造器引用(基于lambda)
方法引用(Method References)
1.当要传递给Lambda体的操作,已有实现的方法了,可以使用方法引用! 2.方法引用可以看作是Lambda表达深层的表达。换句话说,方法引用就是lambda表达式,也就是函数接口的一个实例,通过方法的名字来指向一个方法,可以认为是Lambda表达式的一个语法糖。 3.要求:实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致! 4.格式:(或对象)'::'方法名。 5.如下三种主要使用情况: 对象::实例方法名 类::静态方法名 类::实例方法名//注意;当函数式接口方法的第一个参数是需要引用方法的调用者,并且第二个参数是需要引用方法//的参数(或者无参数)时:ClassName::methodName
构造器引用
格式:ClassName::new 与函数接口相结合,自动与函数式接口中方法兼容。 可以把构造器引用赋值给定义的方法,要求构造器参数列表与接口中抽象方法的参数列表一致!且方法的返回值即为构造器对应类的对象。
数组引用
格式: type[] :: new
为什么这样写
推到与省略如果使用Lambda,那么根据'可推到可以省略原则',无序指定参数类型,无需指定的重写的形式 --》它们都可以被堆到出来,所以就可以省略掉。我们能够使用方法引用,同样也是根据上下文进行推导。 函数式接口是lambda的基础,而方法引用是lambda的优化品。
代码练习
通过对象引用实例方法
@FunctionalInterfacepublic interface Printable { // 定义唯一的抽象方法 void print(String str);}public class MthodTest2 {//定义一个方法成员方法,传递一个字符串,把字符串改为大写输出 public void pringUpperCaseString(String str){ System.out.println(str.toUpperCase()); }}public class MethodTest { // 定义一个静态的方法,方法的参数传递一个函数式接口 public static void pringString(Printable p) { p.print("hello word"); } public static void main(String[] args) { // 传统的lambda表达式写法 pringString((String str) -> { System.out.println(str.toUpperCase());// /* * MthodTest2 test2 = new MthodTest2(); * test2.pringUpperCaseString(str);//传值的动作 通过对象调用方法 */ }); //打印流对象已经确定 PrintStream ps = System.out; //通过对象引用对应的成员方法 pringString(ps::println); /** * 使用方法引用优化lambda * 1.对象必须是已经存在的 * 2.成员方法也是已经存在的pringUpperCaseString * 所以我们就可以使用对象名来来引用我们成员方法 */ // 首先必须是对象已经存在 MthodTest2 test2 = new MthodTest2(); pringString(test2::pringUpperCaseString); }}//在测试类中,定义一个静态方法,静态方法传递一个函数式接口Pringtable,函数式接口当中定义了唯一 -- 抽象方法print,这个print方法接收一个......原文转载:http://www.shaoqun.com/a/896405.html
跨境电商:https://www.ikjzd.com/
菜鸟网:https://www.ikjzd.com/w/1547
hts:https://www.ikjzd.com/w/525
easel:https://www.ikjzd.com/w/1721
函数式接口什么是函数式接口?在java中'有且仅有一个抽象方法的接口',就称为函数式接口。可以通过Lambda表达式来创建该接口的对象。(若Lambda表达式抛出一个受检异常,那么该异常需要在目标接口的抽象方法上进行声明)我们可以在任意函数接口上使用@Functionallnterface注解java8中,这样可以检查它是否是一个函数式接口,同时javadoc也会包含一条声明,说明
抢注商标:https://www.ikjzd.com/w/1053
promoted:https://www.ikjzd.com/w/971
捷汇:https://www.ikjzd.com/w/419
海带宝:https://www.ikjzd.com/w/1548
厦门永定土楼好玩吗?漂亮吗?:http://www.30bags.com/a/401012.html
厦门永定土楼有什么特色小吃吗?:http://www.30bags.com/a/402992.html
厦门永定土楼怎么样啊?门票是多少?:http://www.30bags.com/a/402995.html
厦门永定土楼住宿条件怎么样?:http://www.30bags.com/a/402863.html
深圳bang儿童艺术节在哪里、怎么去:http://www.30bags.com/a/523619.html
深圳桃源之光书馆在哪里(地址+怎么去):http://www.30bags.com/a/523620.html
深圳bang儿童艺术节有什么活动亮点:http://www.30bags.com/a/523621.html
亚马逊Acquco公司给推荐者赠送特斯拉!PayPal将终止Funds Now(基金)计划:https://www.ikjzd.com/articles/146906