【JAVA入门】Day35 - 方法引用

【JAVA入门】Day35 - 方法引用



        方法引用就是“把已经有的方法当作函数式接口中抽象方法的方法体”来使用。

        下面是一个排序方法,它的第二个参数是一个 Comparator 接口的实现类,它是一个函数式接口,因此我们可以用方法引用改写这段代码。

Arrays.sort(arr, new Comparator<Integer>() {
	@Override
	public int compare(Integer o1, Integer o2) {
		return o2 - o1;
	}
});

        直接将已经存在的方法作为第二个参数。

public int subtraction(int n1, int n2) {
	return n2 - n1;
}

Arrays.sort(arr, 比较规则 );

        要使用方法引用有一些限制:

  • 最重要的限制是,引用处必须是一个函数式接口,是可以把接口实现类作为参数传递的。
  • 对于被引用的方法有以下三个限制:
    1.被引用的方法必须已经存在了。
    2.被引用的方法的形参返回值需要跟抽象方法保持一致。
    3.被引用的方法的功能要满足当前需求。

        以下面这段代码为例,我们将演示方法引用相较于之前方法的简洁性。

package Functions;

import java.util.Arrays;
import java.util.Comparator;

public class FunctionDemo1 {
    public static void main(String[] args) {
        //需求:创建一个数组,进行倒序排序
        Integer[] arr = {3,5,4,1,6,2};

        //匿名内部类
        Arrays.sort(arr, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });

        //lambda表达式
        Arrays.sort(arr, (Integer o1, Integer o2) -> {
            return o2 - o1;
        });

        //lambda表达式简化格式
        Arrays.sort(arr, (o1, o2) -> o2 - o1);

        //方法引用
        //表示引用FunctionDemo1类里面的subtraction方法
        //把这个方法当作抽象方法的方法体
        Arrays.sort(arr, FunctionDemo1::subtraction);

        System.out.println(Arrays.toString(arr));
    }


    //被引用的方法可以是Java已经写好的,也可以是一些第三方工具类
    public static int subtraction(int num1, int num2) {
        return num2 - num1;
    }
}

一、方法引用的分类

        方法引用一般就分为三大类:引用静态方法、引用成员方法(这个分为引用其他类的成员方法、引用本类的成员方法、引用父类的成员方法)、引用构造方法。但是也有其他调用方式,比如:使用类名引用成员方法、引用数组的构造方法。

1.引用静态方法

格式:

类名::静态方法

范例:

Integer::parseInt

【练习】集合中有“1”“2”“3”“4”“5”五个字符串数字,要求把他们变成 int 类型。

package Functions;

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

public class FunctionDemo2 {
    public static void main(String[] args) {
        /*
        集合中有“1”“2”“3”“4”“5”五个字符串数字,要求把他们变成 int 类型。
         */

        //1.创建集合并添加元素
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "1","2","3","4","5");

        //2.常规方式
        ArrayList<Integer> list2 = new ArrayList<>();
        for (String s : list) {
            int i = Integer.parseInt(s);
            list2.add(i);
        }

        //3.方法引用
/*        list.stream().map(new Function<String, Integer>() {
            @Override
            public Integer apply(String s) {
                return Integer.parseInt(s);
            }
        }).forEach(s -> System.out.println(s));*/

        //1.方法需要已经存在
        //2.方法的形参和返回值需要跟抽象方法的形参和返回值保持一致
        //3.方法的功能需要把形参的字符串转换成整数
        //引用的是Integer方法中的pareInt()方法

        list.stream().map(Integer::parseInt).forEach(s -> System.out.println(s));
    }
}

2.引用成员方法

格式:

对象::成员方法

① 其他类:

其他类对象::方法名

② 本类:

this::方法名

③ 父类:

super::方法名
2.1 引用其他类的成员方法
package Functions;

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

public class FunctionDemo3 {
    public static void main(String[] args) {
        /*
            只要以张开头的,而且名字是3个字的
         */
        //1.创建集合
        ArrayList<String> list = new ArrayList<>();

        //2.添加数据
        Collections.addAll(list, "张无忌","周芷若","赵敏","张强","张三丰");

        //3.过滤数据
        //完整写法
/*        list.stream().filter(new Predicate<String>() {
            @Override
            public boolean test(String s) {
                    return s.startsWith("张") && s.length() == 3;
            }
        }).forEach(s -> System.out.println(s));*/

        //方法引用写法
        //引用其他类的成员方法
        //已知有另一个类StringOperation中有同样功能方法
        //成员方法需要创建对象来使用,因此要先创建StringOperation类对象
        StringOperation so = new StringOperation();
        list.stream().filter(so::stringJudge);
    }
}
2.2 引用本类和父类的成员方法

        要注意,要引用本类成员方法时,若引用处位于 main() 方法中,它是一个静态方法,静态方法是没有 this 和 super 变量的,因此这里就只能先创建好本类的对象再进行调用。

package Functions;

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

public class FunctionDemo3 {
    public static void main(String[] args) {
        /*
            只要以张开头的,而且名字是3个字的
         */
        //1.创建集合
        ArrayList<String> list = new ArrayList<>();

        //2.添加数据
        Collections.addAll(list, "张无忌","周芷若","赵敏","张强","张三丰");

        //3.过滤数据
        //完整写法
/*        list.stream().filter(new Predicate<String>() {
            @Override
            public boolean test(String s) {
                    return s.startsWith("张") && s.length() == 3;
            }
        }).forEach(s -> System.out.println(s));*/

        //方法引用写法
        //引用本类的成员方法
        //已知本类中有同样功能方法
        //成员方法需要创建对象来使用,因此要先创建本类对象
        list.stream().filter(new FunctionDemo3()::stringJudge);

    }

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

        因此,想要使用 this 和 super,就要在一个非静态方法中,如下面的例子:
        下面的例子中,我们创建了一个窗体,自己在本类中写了一个按钮的事件监听方法,并作为参数传给了按钮的addActionListener()方法,这里就是使用了本类的方法引用,这样做使得该窗体无需实现 ActionListener 接口也能自己实现按钮点击的事件逻辑。

package Functions;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;

public class LoginJFrame extends JFrame {

    //创建一个按钮,点击按钮打印一句话
    JButton go = new JButton("Go");
    public LoginJFrame() {
        //初始化界面
        initJFrame();

        //添加组件
        initView();

        //显示界面
        this.setVisible(true);
    }

    //设置界面
    public void initJFrame() {
        //设置宽高
        this.setSize(400,500);
    }

    //添加组件
    public void initView() {
        go.setBounds(120,274,150,50);
        this.getContentPane().add(go);

        //给按钮添加点击事件
        //该方法形参是一个函数式接口
        //因此可以在此处使用方法引用
        //此时在本类可以自己写一个按钮监听的方法操作,作为方法引用,使用this::method传入
        //这样可以使得该窗体不必实现ActionListener接口也能实现按钮的点击事件
        go.addActionListener(this::method);
    }

    //自己写的按钮监听方法
    public void method(ActionEvent e){
        Object obj = e.getSource();
        if(obj == go){
            System.out.println("go按钮被点击了");
        }
    }
}

        下面的例子中,我们让一个窗体继承了一个 MyJFrame 类,再让 MyJFrame 类继承自 JFrame,然后我们在 MyJFrame 类中自己编写了一个事件监听方法 method,然后在 MyJFrame 类中让按钮的 addActionListener() 方法引用 MyJFrame 类中的事件监听方法 method,这样就实现了引用自父类的成员方法。

父类 MyJFrame:

package Functions;

import javax.swing.*;
import java.awt.event.ActionEvent;

//注意形参和原方法保持一致
public class MyJFrame extends JFrame {
    public void method(ActionEvent e){
        System.out.println("go按钮被点击了");
    }
}

子类 LoginJFrame:

package Functions;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;

public class LoginJFrame extends MyJFrame {

    //创建一个按钮,点击按钮打印一句话
    JButton go = new JButton("Go");
    public LoginJFrame() {
        //初始化界面
        initJFrame();

        //添加组件
        initView();

        //显示界面
        this.setVisible(true);
    }

    //设置界面
    public void initJFrame() {
        //设置宽高
        this.setSize(400,500);
    }

    //添加组件
    public void initView() {
        go.setBounds(120,274,150,50);
        this.getContentPane().add(go);

        //给按钮添加点击事件
        //该方法形参是一个函数式接口
        //因此可以在此处使用方法引用
        //此时在父类可以自己写一个按钮监听的方法操作,作为方法引用,使用super::method传入
        go.addActionListener(super::method);
    }
}

        由上面的例子可以看出,方法引用可以代替函数式接口作为方法的参数传递,利用方法引用可以调整代码结构,省去实现多余的接口。

2.3 引用构造方法

        我们引用构造方法就是为了创建对象。

格式:

类名::new

范例:

Student::new

        以下面的例子为讲解,我们为什么要引用构造方法。
        首先我们在 Student 类中重载了一个构造方法,它是为了适配构造方法引用而设的。

package Functions;

import java.util.Objects;

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

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    //用方法引用,在Student类中重载构造方法,按照上面的public Student apply(String s)的方法体重写
    //str表示流里面的每一个数据
    public Student(String str){
        this.name = str.split("-")[0];
        this.age = Integer.parseInt(str.split("-")[1]);
    }
    
}

        然后我们在测试类中引用构造方法,代替了原来的 map 方法中的函数式接口重写的方法 apply。

package Functions;

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

public class FunctionDemo4 {
    /*
        集合里存储姓名和年龄,比如:张无忌,15
        要求:将数据封装成Student对象并收集到List集合中
     */
    public static void main(String[] args) {
        //1.创建集合对象
        ArrayList<String> list = new ArrayList<>();
        //2.添加数据
        Collections.addAll(list, "张无忌-15","周芷若-14","赵敏-13","张强-20","张三丰-100","张翠山-40","张良-35","王二麻子-37","谢广坤-41");
        //3.封装成Student对象并收集到List集合中
        //String --> Student

/*        //用map方法
        List<Student> newList = list.stream().map(new Function<String, Student>() {
            @Override
            public Student apply(String s) {
                return new Student(s.split("-")[0], Integer.parseInt(s.split("-")[1]));
            }
        }).collect(Collectors.toList());*/

        //用方法引用,在Student类中重载构造方法,按照上面的public Student apply(String s)的方法参数和功能重写
        //构造方法没有返回值,只要保证重载的构造方法对象和你原方法返回的对象保持一致即可
        List<Student> newList = list.stream().map(Student::new).collect(Collectors.toList());
    }
}

        这里要注意的就是,构造方法生成的对象,要和原来方法你要求返回的对象一致(姓名、年龄的格式),且构造方法的参数必须和原方法保持一致(String s)。

2.4 使用类名引用成员方法

        直接用类名引用成员方法。
格式:

类名::成员方法

范例:

String::substring

【练习】集合里有一些字符串,要求变成大写后进行输出。

package Functions;

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

public class FunctionDemo5 {
    public static void main(String[] args) {
        /*
            集合里有一些字符串,要求变成大写后进行输出。

            类名::成员方法
            规则:
            1.需要有函数式接口
            2.被引用的方法必须已经存在
            3.被引用方法的形参,需要跟抽象方法的第二个形参到最后一个形参保持一致,返回值需要保持一致
            4.被引用的方法功能要满足当前需求

            抽象方法形参的详解:
            第一个参数:表示被引用方法的调用者,决定了可以引用哪些类中的方法(比如第一个参数是String类型,那么只能引用String类中的方法)
                        在Stream流当中,第一个参数一般都表示流里面的每一个数据
                        假设流里面的数据是字符串,那么使用这种方式进行方法调用,只能引用String这个类中的方法

            第二个参数到最后一个参数:跟被引用的方法的形参保持一致,如果没有第二个参数,说明被引用的方法需要是无参的成员方法

         */

        //1.创建集合对象
        ArrayList<String> list = new ArrayList<>();
        //2.添加数据
        Collections.addAll(list, "aaa", "bbb", "ccc", "ddd");
        //3.变成大写

        //String --> String
/*        list.stream().map(new Function<String, String>() {
            @Override
            public String apply(String s) {
                return s.toUpperCase();
            }
        }).forEach(s -> System.out.println(s));*/


        //拿流里面的每一个数据,去调用String类中的toUpperCase()方法,方法的返回值就是转换之后的字符串
        list.stream().map(String::toUpperCase).forEach(s -> System.out.println(s));
    }
}

这种方法有一定的局限性:

1.它不能引用所有类中的成员方法。
2.它能引用的类中的成员方法,取决于它的抽象方法(上面代码中的 apply(String s))的第一个参数类型,这个参数是什么类型,它就只能引用这个类中的方法。
3.如果这个抽象方法(apply(String s))没有第二个及以后的参数,那么它就可以引用一个类中的空参方法。

2.5 引用数组的构造方法

格式:

数据类型[]::new

范例:

Integer[]::new

【练习】集合中存储一些整数,然后收集到数组当中。

package Functions;

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

public class FunctionDemo6 {
    public static void main(String[] args) {

        /*
            细节:数组的类型,需要跟流中的数据的类型保持一致
         */

        //1.创建集合
        ArrayList<Integer> list = new ArrayList<>();
        Collections.addAll(list,1,2,3,4,5,6,7,8,9,10);

        //2.收集到数组当中
/*        Integer[] arr = list.stream().toArray(new IntFunction<Integer[]>() {
            @Override
            public Integer[] apply(int value) {
                return new Integer[value];
            }
        });*/

        //此时会将流中每一个数据存入生成的数组中
        list.stream().toArray(Integer[]::new);
    }
}

二、方法引用的例题

【练习1】集合中存一些字符串,比如:张三,23。收集到Student数组当中。

package Functions;

import JavaStudy1.Array.Array;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.stream.Collectors;

public class FuntcionTest1 {
    public static void main(String[] args) {
        /*
            集合中存一些字符串,比如:张三,23。收集到Student数组当中。
         */

        //1.创建集合添加元素
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "张无忌-15","周芷若-14","赵敏-13","张强-20","张三丰-100","张翠山-40","张良-35","王二麻子-37","谢广坤-41");

        //2.先把字符串变成Student对象,然后再把Student对象收集起来
        //String --> Student
        Student[] arr = list.stream().map(Student::new).collect(Collectors.toList()).toArray(Student[]::new);
        System.out.println(Arrays.toString(arr));
    }
}

【练习2】创建集合并添加学生对象,要求获取姓名放到数组中。

package Functions;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;

public class FunctionTest2 {
    public static void main(String[] args) {
        /*
           创建集合并添加学生对象,要求获取姓名放到数组中。
           "张无忌-15","周芷若-14","赵敏-13","张强-20","张三丰-100","张翠山-40","张良-35","王二麻子-37","谢广坤-41"
         */

        //1.创建集合添加数据
        ArrayList<Student> list = new ArrayList<>();
        Collections.addAll(list, new Student("张无忌",15),new Student("周芷若",14),new Student("赵敏",13)
                ,new Student("张强",20),new Student("张三丰",100),new Student("张翠山",40));

        //2.把姓名获取出来生成字符串数组
        //Student --> String
        String[] arr = list.stream().map(Student::getName).toArray(String[]::new);
        System.out.println(Arrays.toString(arr));
    }
}

【练习3】创建集合添加学生对象,把姓名和年龄拼成:张三-23的字符串,并放到数组当中。

package Functions;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;

public class FunctionTest3 {
    public static void main(String[] args) {
        /*
            创建集合添加学生对象,把姓名和年龄拼成:张三-23的字符串,并放到数组当中。
         */

        //1.创建集合并添加学生对象
        ArrayList<Student> list = new ArrayList<>();
        Collections.addAll(list, new Student("张无忌",15),new Student("周芷若",14),new Student("赵敏",13)
                ,new Student("张强",20),new Student("张三丰",100),new Student("张翠山",40));

        //2.取出姓名和年龄拼成字符串
        //Student --> String
        String[] arr = list.stream().map(Student::toString).toArray(String[]::new);
        System.out.println(Arrays.toString(arr));
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值