方法引用

方法引用的概述


匿名内部类注重对象--->Lambda表达式转换为注重函数(表现为将代码简化为只剩下函数的参数和函数体)---->方法引用也注重函数体(在Lambda基础上进行再简化,将精简后的函数函数用已有的方法进行代替)

基础操作

package com.an.a;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.function.Consumer;

//测试方法引用的基本使用
public class FunctionTest {
    public static void main(String[] args) {
        //方法引用:就是把已经有的方法拿过来用,当作函数式接口中抽象方法的方法体
        //需求:创建一个数组,进行倒序排列
        Integer [] arr={2,1,3,6,5,9,8};
        //匿名内部类
        //sort方法第二个参数是一个函数式接口 表示排序规则
     Arrays.sort(arr, new Comparator<Integer>() {
         @Override
         public int compare(Integer o1, Integer o2) {
             return o2-o1;
         }
     });
      //Lambda表达式
        Arrays.sort(arr,(o1,o2)->o2-o1);

      //方法引用
        /*
        1.引用处需要是函数式接口
        2.被引用的方法需要已经存在
        3.别引用的方法的形参和返回值需要和抽象方法的形参和返回值保持一致
        4.被引用的方法需要满足当前的要求(即满足该功能)
         */
        Arrays.sort(arr,FunctionTest::subtraction);//::为引用符号  subtraction静态通过类名调用(虽然subtraction为本类静态方法 但类名不能省略)
        //
        System.out.println();

    }
    //可以是java已经写好的,也可以是一些第三方工具类
    public static int subtraction(int o1,int o2){
        return o2-o1;
    }
}

方法引用的分类

引用静态方法


package com.an.a;

import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Function;

public class FunctionTestA {
    public static void main(String[] args) {
        //方法引用:引用静态方法
        /*
            格式:类名::方法名
            需求:
            集合中有以下数字,要求把他们都变成int类型
            "1" "2" "3" "4" "5" "6"
         */
        //1.创建集合并添加元素
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "1" ,"2","3","4" ,"5", "6");
        //一般方法 遍历集合将里面每个数据转换为int 并装进一个Integer集合中
       /* ArrayList<Integer> newlist = new ArrayList<>();//用于装转换后的数据
        for (String s : list) {
             int i = Integer.parseInt(s);
             newlist.add(i);

        }*/
        //2.使用流处理:将数据放在流上 进行流中数据类型的转换
        list.stream().map(new Function<String, Integer>() {
            @Override
            public Integer apply(String s) {//s表示流中每一个数据
                return Integer.parseInt(s);
            }
        });
        //改为方法引用:用已经存在的方法代替抽象方法的方法体
        //引用的方法满足要求:
        /*
        1.使用的引用的参数为函数式接口
        2.引用的方法需要已经存在
        3.方法的形参和返回值类型必须和抽象方法的形参和返回值类型相同
        4.此处方法的作用可以将一个String-->int
         */
        list.stream().map(Integer::parseInt);//理解为引用(::)Integer类的parseInt方法
        System.out.println(list);



    }
}

引用其他类的成员方法


  • 定义的其他类的引用方法
package com.an.a;

public class StringJuge {
    public boolean stringJuge (String str){
        return str.startsWith("张")&&str.length()==3;
    }
}

  • 程序主体
package com.an.a;

import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Function;
import java.util.function.Predicate;

public class FunctionTestA {
    public static void main(String[] args) {
    /*
    方法引用(引用成员方法)
    格式:其他类:其他类对象::方法名
          本类: this::方法名
          父类: super::方法名
     */
        /*
        需求:
        集合有一些名字,按照要求进行过滤数据
        数据:"张无忌","周芷若","赵敏","张强","张三丰"
        要求:只要以张开头的,而且名字是三个字的
         */
        //1.创建集合
        ArrayList<String> list = new ArrayList<>();
        //2.添加数据
        Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰");
        //3.过滤数据
        //3.1匿名内部类
       /* list.stream().filter(new Predicate<String>() {
            @Override
            public boolean test(String s) {//s表示每一个数据 test为过滤规则

                return  s.startsWith("张")&&s.length()==3;//留下张开头且长度为3的
            }
        }).forEach(s-> System.out.println(s));*/
        //2.使用Lambda:注重函数
       /* list.stream().filter(s -> s
                .startsWith("张") && s.length() == 3)
                .forEach(s -> System.out.println(s));
        //3.方法引用:用已有的方法代替函数
        //3.1引用其他类的方法
        list.stream().
                filter(new StringJuge()::stringJuge)
                .forEach(s-> System.out.println(s));*/
        //注意不能直接引用String中的startWith和length方法:
     //3.2 引用本类的方法
        //list.stream().filter(this::stringJugeA);如果是在静态方法中不能通过this调用
        //也必须用对象调用
        list.stream().filter(new FunctionTestA()::stringJugeA)
                .forEach(s-> System.out.println(s));

    }
    public boolean stringJugeA (String str){
        return str.startsWith("张")&&str.length()==3;
    }
}

引用成员方法用点击事件演示(点击事件还没有学,没有听懂)

引用构造方法

引用构造方法的目的用于创建对象

  • 练习

toList源码也用了方法引用
构造方法和抽象方法对比

Student类(被引用的构造方法所在类)

package Test;

public  class Student {
    private String name;
    private int age;


    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    //设计出一个参数且可以创建对象的构造方法
    public Student(String str){//此时的str表示的是一个元素
        String[] s = str.split(",");
        int age=Integer.parseInt(s[1]);
        String name =s[0];
        this.age=age;
        this.name=name;//不需要返回值因为构造方法调用后将自动生成对象
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}

main方法主体

package com.an.a;

import Test.Student;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class FunctionTestA {
    public static void main(String[] args) {
        /*
            引用构造方法
            格式:类名::new
            目的:
                创建这个类的对象
            需求:
            集合储存姓名和年龄,要求封装成Student对象并收集到List集合中
            方法引用规则:
            1.引用处为函数式接口
            2.被引用的方法必须已经存在
            3.被引用的方法的形参和返回值,必须和抽象方法保持一致
            4.被引用的方法的功能要满足当前需求
         */
        //分析:放到流中,调用map转换数据类型,然后调用collect用List接收
        //1.创建集合
        ArrayList<String> list = new ArrayList<>();
        //2.添加数据
        Collections.addAll(list,"张无忌,12","周芷若,24","赵敏,27","赵无极,23");
        //3.用匿名内部类型实现
        final List<Student> list1 = list.stream().map(new Function<String, Student>() {
            @Override
            public Student apply(String s) {
                String[] str = s.split(",");
                String name = str[0];
                int age = Integer.parseInt(str[1]);
                return new Student(name, age);
            }
        }).collect(Collectors.toList());
      //2.使用方法引用:引用构造方法
      //  3.被引用的方法的形参和返回值,必须和抽象方法保持一致
      //  4.被引用的方法的功能要满足当前需求
        /*
        我们的抽象方法作用是创建一个对象,而引用构造方法作用也创建一个对象(作用相同)
         */
        //而抽象方法的形参为一个参数(我们的构造方法也要满足一个参数)

   List<Student> list2 = list.stream().map(Student::new)
           .collect(Collectors.toList());
        System.out.println(list2);
    }
}

思考感悟:只有是创建对象的方法才需要引用构造方法,引用的构造方法不需要返回值和抽象方法相同,抽象方法返回对象,构造方法本身也返回一个对象,但它没有返回值

  • toList底层

    toList底层也是用了引用

  • 方法引用基础

  • 方法引用拓展

拓展一:使用类名引用成员方法

  • 练习

如果是一般的引用,可以引用所有类的符合规定的方法,但是使用类名引用成员方法,可以引用的类的方法有限制

引用的方法和抽象方法对比

注意方法引用只用写引用方法的方法名,连()都不用写

package com.an.a;

import Test.Student;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class FunctionTestA {
    public static void main(String[] args) {
        /*
        方法引用(类名引用成员方法)
        格式:
            类名::成员方法
         需求:
            集合里面一些字符串,要求变成大写后进行输出
         */
        //1.创建集合
        ArrayList<String> list = new ArrayList<>();
        //2.添加数据
        Collections.addAll(list,"aaa","bbb","ccc","ddd");
        //3.变成大写后进行输出
        //思路:遍历集合,将每个元素变成大写后输出
        //使用Stream流实现(单纯使用集合也可以实现):用map进行数据转换 String->String
       /* list.stream().map(new Function<String, String>() {
            @Override
            public String apply(String s) {//s表示每个元素
                return s.toUpperCase();//将该元素变成大写并返回
            }
        });*/
        /*
        类名引用成员方法的规则:
        1.需要有函数式接口
        2.被引用的方法必须已经存在
        3.被引用方法的形参,需要跟抽象方法的第二个形参到最后一个形参保持一致,返回值保持一致
        4.被引用方法的功能需要满足当前需求
         */
        /*
        抽象方法形参详解:
        第一个参数:表示被引用方法的调用者,决定了可以引用那些类中的方法
                在stream流中,第一个参数一般都表示流里面的每一个数据
                假设 流里面的数据是字符串,那么使用这种方式进行方法引用,只能引用String这个类
                中的方法(数据的类型(一般第一个参数)和可以调用的类一致)
         第二个参数到最后一个参数:跟被引用方法的形参保持一致,如果没有
         第二个参数,说明被引用的方法需要是无参的成员方法
         */
        //使用方法引用
        //拿着流里面的每一个数据,去调用String类中的toUpperCase,返回值就是转换后的结果
        list.stream().map(String::toUpperCase).forEach(s-> System.out.println(s));
        //局限性:
        /*
        不能引用所用类中的成员方法
        这个跟抽象方法的第一个参数有关,这个参数是什么类型的,那么就只能引用这个类中的方法
         */
    }
}

引用数组的构造方法


其实在java的底层数组也存在构造方法,引用数组的构造方法其实就是创建了一个数组

package com.an.a;

import Test.Student;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class FunctionTestA {
    public static void main(String[] args) {
      /*
      方法引用(引用数组中的构造方法)
      格式:
        数据类型[]::new
       目的:创建一个指定类型的数组
       需求:在集合中储存一些并收集到数组当中
       */
        //1.创建集合并添加元素
        ArrayList<Integer> list = new ArrayList<>();
        Collections.addAll(list,1,2,3,4,5,6);
        //2.把集合内的数据收集到数组中
        //思路:就是将集合转换为数组
        Integer[] arr = list.stream().toArray(new IntFunction<Integer[]>() {//该处泛型为转换后数组的类型
            @Override
            public Integer[] apply(int value) {//value表示元素的个数
                return new Integer[value];//元素个数个数组长度一致
            }
        });
       //用引用数组的构造方法实现
        /*
        该抽象方法本质上是创建一个数组,而数组的创建在java底层也是使用的构造方法
        索引可以通过引用数组的构造方法来实现
         */
        Integer[] arr1 = list.stream().toArray(Integer[]::new);//调用数组的构造将会创建长度和元素个数相同的构造方法
        //在java底层已经存在构造方法,我们并不需要关心
    }
}

方法引用综合小练习

练习1--转成自定义对象并收集到数组

  • Student类
package Test;

public  class Student {
    private String name;
    private int age;


    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    //设计出一个参数且可以创建对象的构造方法
    public Student(String str){//此时的str表示的是一个元素
        String[] s = str.split(",");
        int age=Integer.parseInt(s[1]);
        String name =s[0];
        this.age=age;
        this.name=name;//不需要返回值因为构造方法调用后将自动生成对象
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}
  • 程序主体
package com.an.a;

import Test.Student;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class FunctionTestA {
    public static void main(String[] args) {
     /*
     需求:
     集合储存一些字符串的数据,比如:张三,23
     收集到Student类型的数组中(使用方法引用完成)
      */
        //创建集合并添加数据
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"张无忌,23","周芷若,26","天山童姥,3","张山峰,109");
        //思路分析:类型转换String->Student(map) 转换为数组(toArray)
       Student[] arr = list.stream().map(Student::new).toArray(Student[]::new);
        System.out.println(Arrays.toString(arr));

    }
}

方法引用技巧:
思考:1.现在有没有一个方法符合我当前的需求
2.如果有这个方法,这个方法是否满足引用规则
静态方法:类名::方法名
成员方法:分情况考虑
构造方法:类名::new
练习2---获取部分属性并收集到数组

  • Student类
package Test;

public  class Student {
    private String name;
    private int age;


    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    //设计出一个参数且可以创建对象的构造方法
    public Student(String str){//此时的str表示的是一个元素
        String[] s = str.split(",");
        int age=Integer.parseInt(s[1]);
        String name =s[0];
        this.age=age;
        this.name=name;//不需要返回值因为构造方法调用后将自动生成对象
    }

    /**
     * 获取
     * @return name
     */
    //该方法满足引用规则
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}
  • 程序主体
package com.an.a;

import Test.Student;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class FunctionTestA {
    public static void main(String[] args) {
    /*
    需求:
        创建集合并添加学生对象
        学生对象属性为:name,age
        要求:
            获取姓名并添加到数组中(使用方法引用完成)
     */
        //1.创建集合并添加学生对象

        ArrayList<Student> list = new ArrayList<>();
        list.add(new Student("zhangsan",23));
        list.add(new Student("lisi",24));
        list.add(new Student("wangwu",25));
        //2.获取姓名并添加到数组中(使用方法引用完成)
        //分析:过滤出姓名 用数组收集(转换成数组)错误思路:过滤只能以元素的层级进行过滤
        //思路:进行类型转换(Student->String) 然后用数组收集
     /*   final String[] arr = list.stream().map(new Function<Student, String>() {
            @Override
            public String apply(Student stu) {//表示每一个元素
                return stu.getName();
            }
        }).toArray(String[]::new);*/
        //改写成方法引用
        String[] arr = list.stream().map(Student::getName).toArray(String[]::new);
        //map正好可以引用Student类中的getName方法  使用的是 类名::成员方法
        System.out.println(Arrays.toString(arr));

    }
}

posted @ 2023-01-10 15:56  一往而深,  阅读(128)  评论(0)    收藏  举报