函数式编程、Lambda作为方法参数和返回值、方法引用

1.1 函数式编程

1.2 Lambda表达式的延迟执行

  • 使用Lambda表达式的主要原因是:将代码的执行延迟到一个合适的时间点。所有的Lambda表达式都是延迟执行的。
  • 因为有些场景的代码执行后,结果不一定会被使用,从而造成性能浪费。而lambda表达式是延迟执行的,这正好作为解决方案,提升性能。

1.3 性能浪费的日志案例: 

  • Lambda特点:延迟执行==>需要的时候才执行
  • 用户日志:哪个用户在什么时间访问了哪个页面
  • 系统日志
  • 案例:在实际开发的过程中,我们需要记录日志,有时候我们只需要获取到用户的日志,而系统日志则只需要保存到某个数据库中。在下面的案例中,假设 type = 1;为用户日志。type = 2;系统日志。在调用方法的时候,当type = 1时才输出message信息, 当type=2时,不执行if(type ==1){System.out.println(message);},传过来的字符串没有用,但是我们却做了字符串拼接, log(2,usename+time+page);却没有用到拼接产生的结果,浪费性能。为了解决这个问题,我们使用Lambda表达式延迟执行的特点,当我们需要用的时候才进行拼接,这样就避免了性能浪费的问题。
public class LambdaDemo01 {
    public static void main(String[] args) {
        //用户名
        String usename = "jack";
        //时间
        long time = System.currentTimeMillis();
        //页面
        String page = "index.html";

        //用户日志、系统日志
        log(1,usename+time+page);//jack1534255135982index.html
        log(2,usename+time+page);
    }
    /*
        type = 1;用户日志
        type = 2;系统日志
     */
    public static void log(int type,String message){
        if(type ==1){
            System.out.println(message);
        }
    }
}

 1.4使用Lambda表达式改进

  • 小结:只要方法参数类型和返回值类型是函数式接口,则可以使用Lambda表达式作为方法参数或返回值
@FunctionalInterface
interface BuildMessage{
    String buildMsg();
}
public class LambdaDemo01 {
    public static void main(String[] args) {
        //用户名
        String usename = "jack";
        //时间
        long time = System.currentTimeMillis();
        //页面
        String page = "index.html";

        //用户日志、系统日志
      /*log(1, new BuildMessage() {
          @Override
          public String buildMsg() {
              System.out.println("此处高能..");
              return usename + time +page;
          }
      });*/
        System.out.println("--------------");
        //使用Lambda表达式进行改进
        log(1,()->{ return usename + time +page; });
    }
    /*
        type = 1;用户日志
        type = 2;系统日志
     */
    public static void log(int type,BuildMessage bm){
        if(type ==1){
            String message = bm.buildMsg();
            System.out.println(message);
        }
    }
}


2.1 方法引用概述

  • 什么是方法引用
  1. JDK1.8新特性。
  2. 通过类名或对象名引用已经存在的方法来简化Lambda表达式。
  • 有函数式接口 ==>Lambda ==>方法引用
  • 方法引用的格式
  1. 类名::方法名
  2. 对象名::方法名
  • 方法引用的类型
  1. 静态方法引用
  2. 对象方法引用
  3. 构造方法引用
  4. 特定类型的实例方法引用
  • 方法引用的原理
  1. 创建函数式接口的匿名内部类对象
  2. 重写接口中的抽象方法并在抽象方法中调用被引用的方法
  • 方法引用的好处
  1. 简化Lambda表达式
  2. 可以重复利用已经存在的方法

 3.1 方法引用的四种类型

3.2.1 静态方法引用

  • 静态方法引用格式:类名::静态方法名
  • 静态方法引用的注意事项:
  1. 被引用方法的参数列表要和函数式接口中抽象方法的参数列表一致。
  2. 如果函数式接口中的抽象方法有返回值类型,则被引用的方法也必须有相同的返回值类型。
  3. 如果函数式接口中的抽象方法没有返回值,则被引用的方法可以有返回值也可以没有返回值。
  • 示例代码: 
@FunctionalInterface
public interface ArrayHelper {
    //返回数组中的最大值
    int maxValue(int[] arr);
}



/*
    数组工具类
 */
public class ArrayUtils {
    /*
     *返回数组中的最大值
     * @param arr 数组
     * @return 最大值
     */
    public static int getMax(int[] arr){
        int maxValue = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if(arr[i]>maxValue){
                maxValue = arr[i];
            }
        }
        return maxValue;
    }
}




public class MethodRefDemo01 {
    public static void main(String[] args) {
        //定义数组
        int[] arr = {12,34,45,56,34,3,23,12};

        //使用匿名内部类创建ArrayHelper接口实现类对象
        ArrayHelper ah01 = new ArrayHelper() {
            @Override
            public int maxValue(int[] arr) {
               int maxValue = arr[0];
                for (int i = 0; i < arr.length; i++) {
                    if(arr[i]>maxValue){
                        maxValue = arr[i];
                    }
                }
                return maxValue;
            }
        };
        //调用方法获得数组的最大值
        int maxValue01 = ah01.maxValue(arr);
        System.out.println("maxValue01="+maxValue01);
        System.out.println("--------------------");
        //使用lambda表达式简化匿名内部类
        ArrayHelper ah02 = arr1 -> { int maxValue = arr[0];
            for (int i = 0; i < arr.length; i++) {
                if(arr[i]>maxValue){
                    maxValue = arr[i];
                }
            }
            return maxValue;
        };
        //调用方法获取数组中的最大值
        int maxValue02 = ah02.maxValue(arr);
        System.out.println("maxValue02="+maxValue02);
        System.out.println("-------------------------");
        //在Lambda表达式中调用工具类已经存在的方法
        ArrayHelper ah03 = arr1 -> ArrayUtils.getMax(arr);
        //调用方法获得数组中的最大值
        int maxValue03 = ah03.maxValue(arr);
        System.out.println("maxValue03="+maxValue03);
        System.out.println("-------------------------");
        //当Lambda表达式中仅仅调用了一个已经存在的方法时,可以考虑使用方法引用简化Lambda表达式
        ArrayHelper ah04 = ArrayUtils::getMax;
        //调用方法获得数组中的最大值
        int maxValue04 = ah04.maxValue(arr);
        System.out.println("maxValue04="+maxValue04);

    }
}
 ArrayHelper ah04 = ArrayUtils::getMax;等价下面代码:
        1.创建函数式接口的匿名内部类对象
       ArrayHelper ah04 = new ArrayHelper() {
           int maxValue(int[] arr) {
               //2.调用被引用的方法
               return ArrayUtils.getMax(arr);
           }
       }

 int  maxValue04 = ah04.maxValue(arr);

3.2.2 对象方法引用

  • 对象方法引用格式:对象名::非静态方法名
  • 对象方法引用的注意事项:
  1. 被引用方法的参数列表要和函数式接口中抽象方法的参数列表一致。
  2. 如果函数式接口中的抽象方法有返回值类型,则被引用的方法也必须有相同的返回值类型。
  3. 如果函数式接口中的抽象方法没有返回值,则被引用的方法可以有返回值也可以没有返回值。
@FunctionalInterface
public interface NumHelper {
    //获得a到b之间的随机数
    int nextAtoB(int a,int b);
}


import java.util.Random;

public class MyRandom {
    // 返回a到b之间的随机数,比如 a = 100,b == 200 返回100到200之间的随机数
    public int nextIntAtoB(int a,int b){
        //创建随机数对象
        Random ran = new Random();
        int i = ran.nextInt(b-a+ 1) + a;
        return i;
    }
}


import java.util.Random;

public class MethodRef01 {
    public static void main(String[] args) {
        //使用匿名内部类创建NumHelper接口实现类对象
        NumHelper nh1 = new NumHelper() {
            @Override
            public int nextAtoB(int a, int b) {
                //创建随机数对象
                Random ran = new Random();
                return ran.nextInt(b-a+ 1) + a;
            }
        };
        //获得200到300之间的随机数
        System.out.println(nh1.nextAtoB(200,300));
        //使用Lambda表达式简化匿名内部类
        NumHelper nh2 = (a,b)->{ //创建随机数对象
            Random ran = new Random();
            return ran.nextInt(b-a+ 1) + a;
        };
        //获得300到400之间的随机数
        System.out.println(nh2.nextAtoB(300,400));

        //创建MyRandom对象
        MyRandom myRandom = new MyRandom();
        //在Lambda表达式中调用已经存在的方法
        NumHelper nh3 = (a,b)-> myRandom.nextIntAtoB(a,b);
        //获得400到500之间的随机数
        System.out.println(nh3.nextAtoB(400,500));

        //使用方法引用简化Lambda表达式
        NumHelper nh4 = myRandom::nextIntAtoB;
        //获得500到600之间的随机数
        System.out.println(nh4.nextAtoB(500,600));
    }
}



/*
    使用方法引用简化Lambda表达式
    NumHelper nh04 = myRandom::nextIntAToB;等价下面代码:
    NumHelper nh04 = new NumHelper(){
        int nextAToB(int a,int b){
           return myRandom.nextIntAtoB(a,b);
        }
   }
 */


3.2.3 类构造方法的引用:

  • 构造方法引用的格式:类名::new
  • 构造方法引用注意事项:被引用的类必须存在一个构造方法的参数列表和函数式接口中的抽象方法参数列表相同。
public class MethodRefDemo02 {
    public static void main(String[] args) {
        //使用匿名内部类创建CarFactory接口的实现类对象
        CarFactory cf1 =new CarFactory() {
            @Override
            public Car makeCar(String brand) {
                return new Car(brand);
            }
        };
        //生产一部宝马
        Car BWM = new Car("宝马");
        System.out.println(BWM);
        // 使用lambda表达式简化匿名内部类
        CarFactory cf2 = brand ->  new Car(brand);
        //生产一部奥迪
        Car audi = new Car("奥迪");
        System.out.println(audi);
        // 使用方法引用简化lambda表达式
        CarFactory cf3 = Car::new;
        Car Cayenne = new Car("卡宴");
        System.out.println(Cayenne);
    }
}
public class Car {
    private String brand;

    public Car() {
    }

    public Car(String brand) {
        this.brand = brand;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    @Override
    public String toString() {
        return "Car{" +
                "brand='" + brand + '\'' +
                '}';
    }
}
@FunctionalInterface
public interface CarFactory {
    Car makeCar(String brand);
}

3.2.4 数组构造方法引用

  • 数据类型【】::new
@FunctionalInterface
public interface ArrayBuilder {
    // 创建长度为length的整型数组
    int[] buildArray(int length);
}


import java.util.Arrays;

public class MethodRefDemo03 {
    public static void main(String[] args) {
        //使用Lambda表达式创建接口实现类对象
        ArrayBuilder ab01 = length -> new int[length];
        //创建长度为10的数组
        int[] arr1 = ab01.buildArray(10);
        System.out.println(Arrays.toString(arr1));//[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

        //使用方法引用简化Lambda表达式
        ArrayBuilder ab02 = int[]::new;
        //创建长度为5的数组
        int[] arr2 = ab02.buildArray(5);
        System.out.println(Arrays.toString(arr2));//[0, 0, 0, 0, 0]
    }
}

3.2.5 特定类型的实例方法引用:(实例==>对象)(对象方法==>非静态方法)

  • 特定类型的实例方法引用格式:类名::非静态方法
  • 特定类型的实例方法注意事项:
  1. 被引用的方法要比函数式接口中的抽象方法要少一个参数。
  2. 函数式接口中的抽象方法参数列表的第一个参数调用被引用的方法,抽象方法中的其他参数(除了第一个参数以外的其他参数)作为被引用方法的参数传递。
import java.util.Arrays;
import java.util.Comparator;

public class MethodRefDemo04 {
    public static void main(String[] args) {
        String[] stringsArray = {"AA", "James", "Mary",
                "John", "Patricia", "Robert", "aa", "Linda"};
        //对字符串数组排序,不忽略大小写
        Arrays.sort(stringsArray);
        for (String str : stringsArray) {
            System.out.println(str);
        }
        System.out.println("-----------------------------");
        //自定义比较器,对字符串数组排序,忽略大小写
        Arrays.sort(stringsArray, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.compareToIgnoreCase(o2);
            }
        });
        for (String s : stringsArray) {
            System.out.println(s);
        }
        System.out.println("-----------------------------");
        //使用lambda表达式对匿名内部类进行简化
        Arrays.sort(stringsArray,((o1, o2) -> o1.compareToIgnoreCase(o2) ));
        for (String s : stringsArray) {
            System.out.println(s);
        }
        System.out.println("---------------------------------");
        //使用方法引用简化Lambda表达式
        Arrays.sort(stringsArray,String::compareToIgnoreCase);
        for (String s : stringsArray) {
            System.out.println(s);
        }
    }
}

猜你喜欢

转载自blog.csdn.net/Huangyuhua068/article/details/81674941