方法的引用和Lambda详细对比解析

函数式接口:

概念:

函数式接口在java中指:有且只有一个抽象方法的接口.

函数式接口,即使用于函数式编程场景的接口,而java中的函数式编程体现的就是Lambda,所以函数式接口就是

适用于Lambda使用的接口,只要有确保接口中有且仅有一个抽象方法,java中的Lambda才能顺利的进行推导下

去.

格式:

只要保证接口中仅有一个抽象方法就可以.

修饰符 interface 接口名称{
public abstract 返回值类型 方法名称(可选择参数信息);
}

接口中抽象方法的:public abstract 可以省略.

1.@FunctionalInterface注解(只能用于函数式接口)

@Override注解的作用类似,Java 8中专门为函数式接口引入了一个新的注解:@FunctionalInterface

1.1自定义函数式接口

代码如下:

MyFuntionalInterface接口:

@FunctionalInterface
public interface MyFunctionalInterface {
	void lu();
}

对于刚好定义好的接口可以定义作为方法的参数:

package com.heima.hanshushi;
//作为方法的参数
public class Demo01 {
    //使用自定义的函数式接口作为方法参数
    private static void liang(MyFunctionalInterface inter){
        inter.lu();//调用自定义的函数式接口方法
    }
    public static void main(String[] args) {
        //调用使用函数式接口的方法
        //无参数,无返回值
        liang(()->{
            System.out.println("我执行了.");
        });
    }
}

常用的函数式接口

1.Supplier接口(产生一个数据)

java.util.function.Supplier<T>接口仅包含一个无参的方法:T get()。用来获取一个泛型参数指定类型的对象数据。由于这是一个函数式接口,这也就意味着对应的Lambda表达式需要“对外提供”一个符合泛型类型的对象数据。

package com.heima.hanshushi2;

import java.util.function.Supplier;

//函数式接口
public class Demo04 {
    //无参数有返回值
    private static String getString(Supplier<String> a){
        return a.get();
    }
    public static void main(String[] args) {
        String msgA="Hello";
        String msgB="World";
        System.out.println(getString(()->{return msgA+msgB;}));
    }
}

2.求数组的元素的最大值

题目:使用Supplier接口作为方法参数类型,通过Lambda表达式求出Int数组中的最大值,

提示:接口的泛型请使用java.lang.Integer类

package com.heima.hanshushi2;

import java.util.function.Supplier;

public class Demo05 {
    private static void printMax(Supplier<Integer> a) {
        int max = a.get();
        System.out.println(max);
    }

    public static void main(String[] args) {
        //定义一个数组
        int[] array = {10, 23, 22, 4333, 4, 44, 3, 3, 3};
        printMax(() -> {
            int max = array[0];
            for (int i = 1; i < array.length; i++) {
                if (array[i] > max) {
                    max = array[i];
                }
            }
            return max;
        });
    }
}

3.Consumer接口(消费一个数据)

java.util.function.Consumer<T>接口则正好相反,它不是生产一个数据,而是消费一个数据,其数据类型由泛型参数决定。

图解

抽象方法:accept

Consumer接口中包含抽象方法void accept(T t),意为消费一个指定的泛型的数据.

package com.heima.hanshushi2;

import java.util.function.Consumer;

//消费一个数据
public class Demo06 {
    private static void consumeraccept(Consumer<String> a){
        a.accept("Hello");
    }

    public static void main(String[] args) {
        consumeraccept((String s)->{
            System.out.println(s);
        });
        consumeraccept(System.out::println);//方法的引用
    }
}

4.格式化打印信息

练习:格式化打印信息

题目:字符串数组当中存有多条信息,请按照格式“姓名:XX。性别:XX。”的格式将信息打印出来。要求将打印姓名的动作作为第一个Consumer接口的Lambda实例,将打印性别的动作作为第二个Consumer接口的Lambda实例,将两个Consumer接口按照顺序“拼接”到一起。

package com.heima.hanshushi2;

import java.util.function.Consumer;
//消费接口
public class Demo07 {
    private static void printInfo(Consumer<String> a,Consumer<String> b,String[] array){
        for (String s : array) {
            a.andThen(b).accept(s);
        }
    }
    public static void main(String[] args) {
        String[] array={"迪丽热巴,女\", \"古力娜扎,女\", \"马尔扎哈,男"};
        printInfo(s-> System.out.print("姓名:"+s.split(",")[0]),
                  s-> System.out.print(".性别:"+s.split(",")[1]+"."),array
        );
    }
}

自定义函数式接口

1.无参数无返回值

2.有参数有返回值

 

Lambda表达式

执行流程图:

1.Lambda执行图解bda的延迟性:

案例:

性能的浪费的日志案例:

public class Demo01Logger {
    private static void log(int level, String msg) {
        if (level == 1) {
          	System.out.println(msg);
        }
    }

    public static void main(String[] args) {
        String msgA = "Hello";
        String msgB = "World";
        String msgC = "Java";

        log(1, msgA + msgB + msgC);
    }
}

解释:这段代码存在问题,无论级别是否满足,作为Log方法的第二个参数,和第三个参数一定首先会被并如传入方法内.然后进行判断,如果不符合那就白写了.

先进的Lambda的写法:

(抽象方法的参数类型 变量)->{}

2.Lambda使用情况:

2.1作为参数返回值

其实是:函数式接口作为方法的参数类型

            函数式接口作为方法的返回值类型.

代码演示:函数式接口作为方法的参数类型.

package com.heima.hanshushi2;
/*
例如java.lang.Runnable接口就是一个函数式接口,
假设有一个startThread方法使用该接口作为参数,
那么就可以使用Lambda进行传参。
这种情况其实和Thread类的构造方法参数为Runnable没有本质区别。
 */
public class Demo01 {
    private static void startThread(Runnable task){
        new Thread(task).start();
    }

    public static void main(String[] args) {
        startThread(()->System.out.println("线程执行任务"));
    }
}

代码演示:函数式接口作为方法的返回值类型.

package com.heima.hanshushi2;

import java.util.Arrays;
import java.util.Comparator;
/*
类似地,如果一个方法的返回值类型是一个函数式接口,
那么就可以直接返回一个Lambda表达式。
当需要通过一个方法来获取一个
java.util.Comparator接口类型的对象作为排序器时:
 */
public class Demo02 {
    private static Comparator<String> lu(){
        return (a,b)->b.length()-a.length();
    }

    public static void main(String[] args) {
        String[]array={"asdalk","asdas","sasahd"};
        System.out.println(Arrays.toString(array));
        Arrays.sort(array,lu());
        System.out.println(Arrays.toString(array));
    }
}

2.2冗余的Lambda场景

package com.heima.hanshushi2;

public class Demo03 {
   private static void lu(Printable printable){
  //lu方法需要让接口中调用print方法,至于print方法怎么执行的不需要管
       printable.print("你好啊");//调用接口的抽象方法才会执行Lambda表达中的解决方案
                                 //调用方法print方式最终的效果是把"你好啊"打印在控制台上
   }

    public static void main(String[] args) {
        lu((String s)-> System.out.println(s));
          
    }
}

通过方法引用来改进上面代码:

public static void main(String[] args){
  lu(System.out::println);
}

方法的引用:

格式:双冒号 : : 为引用运算符,而他所在的表达式被叫文方法的引用.

什么时候使用方法引用用来替代Lambda表达式(方法的引用使用的场景).

1.如果Lambda表达式指定的解决方案已经在另一个方法中实现了,那么这个时候就可以使用方法的引用来替代.

2.简而言之:如果lambda表达式中的解决方案,就是调用另一个方法,那么这个时候就是可以使用方法的引用来替代.

语义分析:

  • Lambda表达式写法:s -> System.out.println(s);

  • 方法引用写法:System.out::println

第一种语义是指:拿到参数之后经Lambda之手,继而传递给System.out.println方法去处理。

第二种等效写法的语义是指:直接让System.out中的println方法来取代Lambda。两种写法的执行效果完全一样,而第二种方法引用的写法复用了已有方案,更加简洁。

1.通过对象名引用成员方法

格式: 对象::方法名

 成员方法:liang

package com.heima.biji.day01;
//方法
public class MethodRefObject {
    public void liang(String str){
        System.out.println(str.toUpperCase());//打印大写
    }
}

函数式接口:

package com.heima.biji.day01;
@FunctionalInterface
public interface Printable {
    void print(String str);
}

那么当需要使用这个liang这个成员方法来替代Printable接口的Lambda时候,已经具有了MethodRefObject类对象的实例,则可以通过对象名引用成员方法.'

package com.heima.biji.day01;

public class Demo01 {
    private static void  lu(Printable a){
        a.print("Hello");
    }

    public static void main(String[] args) {
        MethodRefObject methodRefObject = new MethodRefObject();
        lu(methodRefObject::liang);
    }
}

2.通过类名称引用静态方法

格式: 类名::静态方法名

函数表达式

package com.heima.biji.day03;
@FunctionalInterface
public interface Lu {
    int calc(int num);
}

Lambda表达式书写

package com.heima.biji.day03;

public class Demo01 {
    private static void liang(int num,Lu lambda){
        System.out.println(lambda.calc(num));
    }
//通过Lambda表达式
    public static void main(String[] args) {
        liang(-10,(int num)->Math.abs(num));
    }
}

方法的引用

package com.heima.biji.day03;

public class Demo02 {
    private static void liang(int num,Lu lambda){
        System.out.println(lambda.calc(num));
    }
    public static void main(String[] args) {
        liang(-10,Math::abs);
    }
}
//- Lambda表达式:n -> Math.abs(n)
//- 方法引用:Math::abs

2.1通过对象名引用成员方法

格式: 对象名::方法名

类:Assistant其中有成员方法lu如下:

package com.heima.biji.day02;

public class Assistant {
    public void lu(String a){
        System.out.println("帮忙处理下文件:"+a);
    }
}

函数式接口:liang 有参数无返回值

package com.heima.biji.day02;
//函数式接口
public interface liang {
    void help(String b);
}

通过对象名引用成员方法的使用场景代码为:

package com.heima.biji.day02;

public class Demo01 {
    private static void work(liang c){
        c.help("机密文件");
    }
    public static void main(String[] args) {
        Assistant assistant = new Assistant();
        work(assistant::lu);
    }
}
//打印结果:帮忙处理下文件:机密文件

类名称引用静态方法:

题目:

假设有一个助理类Assistant,其中含有成员方法dealFile如下:

package com.heima.biji.day04;

public class StringUtils {
    public static boolean isBlank(String str){
        return str==null||"".equals(str.trim());
    }
}

函数表达式:

package com.heima.biji.day04;
@FunctionalInterface
public interface lu {
    boolean liang(String str);
}

通过对象名引用成员方法的使用场景代码为:

package com.heima.biji.day04;

public class StringUtils {
    public static boolean isBlank(String str){
        return str==null||"".equals(str.trim());
    }
}

3.通过super引用父类的成员方法

格式: supper::父类方法名

函数式接口

package com.heima.biji.day05;

public interface lu {
    void liang();
}

父类中成员方法sayHello内容

package com.heima.biji.day05;

public class Fu {
    public  void  sayHello(){
        System.out.println("Hello");
    }
}

子类中内容

package com.heima.biji.day05;

public class Demo01 extends Fu{
    //重写父类中的方法
    @Override
    public void sayHello() {
        method(()->super.sayHello());
        method(super::sayHello);
    }
    private static void method(lu lambda){
        //类名称.抽象方法名
        lambda.liang();
        System.out.println("I love you");
    }

    public static void main(String[] args) {

    }

}

注意:

  • Lambda表达式:() -> super.sayHello()

  • 方法引用:super::sayHello

4.通过this引用成员方法

格式: this::本类方法名

package com.heima.biji.day06;
//
@FunctionalInterface
public interface Richable {
    void  buy();
}
package com.heima.biji.day06;

public class Husband1 {
    private void buyHouse() {
        System.out.println("买套房子");
    }

    private void marry(Richable lambda) {
        lambda.buy();
    }

    public void beHappy() {
        marry(this::buyHouse);
    }
}

5.类的构造器的引用

格式: 类名::new

但是通过构造器引用,有更好的写法

public class Husband {
    private void buyHouse() {
      	System.out.println("买套房子");
    }

    private void marry(Richable lambda) {
      	lambda.buy();
    }

    public void beHappy() {
      	marry(this::buyHouse);
    }
}

在这个例子中,下面两种写法是等效的:

  • Lambda表达式:() -> this.buyHouse()

  • 方法引用:this::buyHouse

6.数组构造器的引用

格式: 数据类型::new ===> 数据类型[] :: new

方法的引用书写

  • Lambda表达式:length -> new int[length]

  • 方法引用:int[]::new

  • 注意:数组的构造器引用,可以和Java 8的Stream API结合,在一定程度上“解决”集合中toArray方法的泛型擦除问题。

public class Demo12ArrayInitRef {
    private static int[] initArray(int length, ArrayBuilder builder) {
      	return builder.buildArray(length);
    }

    public static void main(String[] args) {
      	int[] array = initArray(10, int[]::new);
    }
}

/*总结:
通过对象引用成员方法 : 对象名::方法名
    当Lambda的解决方案是在调用一个对象的成员方法,这个时候就可以使用方法引用替换Lambda表达式

通过类名引用静态方法 : 类名::方法名
    当Lambda的解决方案是在调用一个类的静态方法,这个时候就可以使用方法引用替换Lambda表达式

通过super引用父类方法: super::父类方法名
    当Lambda的解决方案是在调用一个父类的方法,这个时候就可以使用方法引用替换Lambda表达式

通过this引用本类方法: this::本类方法名
    当Lambda的解决方案是在调用一个本类的方法,这个时候就可以使用方法引用替换Lambda表达式

方法引用的场景:
        如果Lambda表达式指定的解决方案已经在另一个方法中实现了,那么这个时候就可以使用方法引用替换Lambda表达式
        简而言之:如果Lambda表达式中的解决方案,就是调用另一个方法,那么这个时候就可以使用方法引用替换Lambda表达式
 */

 

 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值