2.3.引用成员方法

一.引用成员方法的格式:

  • 注:这里引用的成员方法一定是非静态的,因为引用静态方法有专门的格式即类名::静态方法名

  • 静态方法中是没有this的,所以对于本类中使用this::方法名实现方法引用,必须在非静态方法中

  • 静态方法要想使用方法引用来调用本类方法的话,就必须先创建本类的对象,再用该对象去引用方法(这个与 静态内容不能和非静态内容互相调用 无关)

  • 非静态方法要想使用方法引用来调用本类方法的话,可以使用this::方法名实现方法引用,也可以先创建本类的对象,再用该对象去引用方法


二.练习一:包含引用其他类的成员方法和引用本类的成员方法

题目:

需求:集合中有一些数据,按照要求过滤数据

数据:"张无忌","周芷若","赵敏","张强","张三丰"(数据为名字,名字要是字符串型)

要求:只要以张开头,而且名字是3个字的数据

解法一:

import java.util.ArrayList;
import java.util.Collections;
​
public class Main3 {
    public static void main(String[] args) {
        //1.创建集合
        ArrayList<String> list=new ArrayList<>();
​
        //2.添加数据
        Collections.addAll(list,"张无忌","周芷若","赵敏","张强","张三丰");
​
        //3.过滤数据
        list.stream()
                .filter(s->s.startsWith("张")) //表示以张开头的数据留下,其他过滤掉
                .filter(s->s.length()==3) //表示数据长度为3的留下,其他过滤掉
                .forEach(s -> System.out.println(s));
    }
}
​

解法二:

package com.itheima.a01myfunction;
​
import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Predicate;
​
public class FunctionDemo3 {
    public static void main(String[] args) {
        //1.创建集合
        ArrayList<String> list=new ArrayList<>();
​
        //2.添加数据
        Collections.addAll(list,"张无忌","周芷若","赵敏","张强","张三丰");
​
        //3.过滤数据
        list.stream().filter(new Predicate<String>() {
            @Override
            public boolean test(String s) { //s代表流里的每一个数据即集合list中的每一个数据
                if(s.charAt(0)=='张'&&s.length()==3) return true; //返回true,表示以张开头且数据长度为3的数据留下,其他过滤掉
                return false;
            }
        }).forEach(s-> System.out.println(s));
    }
}

解法三:

import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Predicate;
​
public class Main3 {
    public static void main(String[] args) {
        //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));
    }
}
​

解法四:把解法三使用方法引用改写->引用其他类的成员方法

未改写前:

import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Predicate;
​
public class Main3 {
    public static void main(String[] args) {
        //1.创建集合
        ArrayList<String> list=new ArrayList<>();
​
        //2.添加数据
        Collections.addAll(list,"张无忌","周芷若","赵敏","张强","张三丰");
​
        //3.过滤数据
        /*
        *  使用方法引用的前提:
        *   1.引用处必须是函数式接口:此时Predicate接口就是函数式接口
        *   2.要引用的方法必须存在(可以是Java写好的,也可以是人为写好的,也可以是一些第三方的工具类),
        *     如果不存在,就需要手动写一个
        *     ->此时Predicate接口中的方法test的形参是字符串型,返回值是布尔类型,而且方法的作用是只要以张开头,而且名字是3个字的数据
        *       显然Java没有提供这样的方法(String类里的equals方法不行,因为该equals方法的形参是Object型),人为也没有写过
        *     ->所以可以自己写一个符合要求的方法
        *
        * */
        list.stream().filter(new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.startsWith("张") && s.length()==3;
            }
        }).forEach(s -> System.out.println(s));
    }
}

使用方法引用后:

import com.itheima.a01myfunction.StringOperation;
​
import java.util.ArrayList;
import java.util.Collections;
​
public class Main3 {
    public static void main(String[] args) {
        //1.创建集合
        ArrayList<String> list = new ArrayList<>();
​
        //2.添加数据
        Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰");
​
        //3.过滤数据
        /*
         *  使用方法引用的前提:
         *   1.引用处必须是函数式接口:此时Predicate接口就是函数式接口
         *   2.要引用的方法必须存在(可以是Java写好的,也可以是人为写好的,也可以是一些第三方的工具类),
         *     如果不存在,就需要手动写一个
         *     ->此时Predicate接口中的方法test的形参是字符串型,返回值是布尔类型,而且方法的作用是只要以张开头,而且名字是3个字的数据
         *       显然Java没有提供这样的方法(String类里的equals方法不行,因为该equals方法的形参是Object型),人为也没有写过
         *     ->所以可以自己写一个符合要求的方法
         *
         * */
        //方法一:
        list.stream()
                .filter(new StringOperation()::stringJudge)
                .forEach(s -> System.out.println(s));
        /*方法二:
        StringOperation so=new StringOperation();
        list.stream().filter(so::stringJudge).forEach(s -> System.out.println(s));*/
    }
}
​
package com.itheima.a01myfunction;
​
public class StringOperation {
    /* 在StringOperation类里写一个方法,
       该方法要被引用在Predicate接口创建的匿名内部类的test方法处
        ->要写的方法返回值类型需要和test方法一致:该方法需要返回布尔类型
        ->要写的方法的命名无需和test方法一致,见名知意即可
        ->要写的方法的形参需要和test方法一致,形参的命名无需和test方法的形参名一样,见名知意即可
        ->要写的方法的作用需要和test方法一致,test方法的作用是只要以张开头,而且名字是3个字的数据,
          所以要写的方法的作用也是如此
     */
    public boolean stringJudge(String str){
        return str.startsWith("张") && str.length()==3;
    }
    /*
    * 要被引用的stringJudge方法是一个普通的成员方法,非静态,普通的成员方法需要用对象来调用,
    * 而且要被引用的stringJudge方法和引用处test方法所属类不是同一个类,
    * 所以在函数式接口Predicate处需要创建一个对象再引用stringJudge方法
    *
    * */
}

解法五:把解法三使用方法引用改写->引用本类的成员方法

  • 静态方法中是没有this的,所以对于本类中使用this::方法名实现方法引用,必须在非静态方法中

  • 静态方法要想使用方法引用来调用本类方法的话,就必须先创建本类的对象,再用该对象去引用方法(这个与 静态内容不能和非静态内容互相调用 无关)

  • 非静态方法要想使用方法引用来调用本类方法的话,可以使用this::方法名实现方法引用,也可以先创建本类的对象,再用该对象去引用方法

import java.util.ArrayList;
import java.util.Collections;
​
public class Main4 {
    public static void main(String[] args) {
        //1.创建集合
        ArrayList<String> list = new ArrayList<>();
​
        //2.添加数据
        Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰");
​
        //3.过滤数据
        /*
         *  使用方法引用的前提:
         *   1.引用处必须是函数式接口:此时Predicate接口就是函数式接口
         *   2.要引用的方法必须存在(可以是Java写好的,也可以是人为写好的,也可以是一些第三方的工具类),
         *     如果不存在,就需要手动写一个
         *     ->此时Predicate接口中的方法test的形参是字符串型,返回值是布尔类型,而且方法的作用是只要以张开头,而且名字是3个字的数据
         *       显然Java没有提供这样的方法(String类里的equals方法不行,因为该equals方法的形参是Object型),人为也没有写过
         *     ->所以可以自己写一个符合要求的方法
         *
         * */
        //静态方法中是没有this的,所以在静态方法main中list.stream().filter(this::stringJudge);是错的
        //此时静态方法要想使用方法引用来调用本类方法的话,就必须创建本类的对象,用该对象再去引用方法
        list.stream().filter(new Main4()::stringJudge).forEach(s -> System.out.println(s));
    }
​
    /* 在本类里写一个方法,
      该方法要被引用在Predicate接口创建的匿名内部类的test方法处
       ->要写的方法返回值类型需要和test方法一致:该方法需要返回布尔类型
       ->要写的方法的命名无需和test方法一致,见名知意即可
       ->要写的方法的形参需要和test方法一致,形参的命名无需和test方法的形参名一样,见名知意即可
       ->要写的方法的作用需要和test方法一致,test方法的作用是只要以张开头,而且名字是3个字的数据,
         所以要写的方法的作用也是如此
    */
    public boolean stringJudge(String str){
        return str.startsWith("张") && str.length()==3;
    }
    /*
     * 要被引用的stringJudge方法是一个普通的非静态成员方法,普通的成员方法需要用对象来调用,
     * 而且要被引用的stringJudge方法和引用处里的test方法是同一个类,又因为引用处在静态方法main里,
     * 所以只需要在引用处先创建本类对象再引用方法stringJudge即可
     *
     * */
​
}
​

三.引用本类或父类的成员方法:

  • 静态方法中是没有this和super的,所以引用本类的成员方法或引用父类的成员方法时,引用处不能是静态方法


四.练习二:包含引用本类的成员方法和引用父类的成员方法

题目:

需求:GUI界面中点击事件的方法引用写法

前言:

解答:

未使用方法引用前:

测试类:

package com.itheima.a02game;
​
public class App {
    public static void main(String[] args) {
        new LoginJFrame();
    }
}

LoginJFrame类:

package com.itheima.a02game;

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

public class LoginJFrame extends JFrame implements ActionListener {
    /* go对象起初只被定义在initView方法中,但需求中在actionPerformed方法中也需要用到go对象,
       而且在运行时需要initView方法比actionPerformed方法先运行,
       意味着如果actionPerformed方法此时要想用到go对象,就必须从initView方法中获取,此时只能通过形参传递go对象,
       但actionPerformed方法是Java已经写好的,而且是被重写的,Java写actionPerformed方法时形参中没有关于go对象的,
       所以把go对象只定义在initView方法中是无法传递到actionPerformed方法中的,
       此时只能把创建go对象的语句设置为全局变量供全局使用*/
    JButton go=new JButton("imagine\\Go.jpg");

    public LoginJFrame(){
        //设置图标
        setIconImage(Toolkit.getDefaultToolkit().getImage("imagine\\logo.jpg"));

        //设置界面
        initJframe();

        //添加组件
        initView();

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

    //设置界面
    public void initJframe(){
        //设置标题
        this.setTitle("随机点名器");
        //设置大小
        this.setSize(400,500);
        //设置关闭模式
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //设置窗口无法进行调节
        this.setResizable(false);
        //设置界面居中
        this.setLocationRelativeTo(null);
        //取消内部默认居中设置
        this.setLayout(null);
        //设置背景颜色
        this.getContentPane().setBackground(Color.white);
        //设置置顶
        this.setAlwaysOnTop(true);
    }

    //添加组件
    public void initView(){
        JLabel image=new JLabel(new ImageIcon("imagine\\点名.jpg"));
        image.setBounds(100,50,174,174);
        this.getContentPane().add(image);

        //go对象属于全局变量
        go.setFont(new Font(null,1,20));
        go.setBounds(120,274,150,50);
        go.setBackground(Color.WHITE);
        //需求:点击go对象后在控制台要有数据打印->给go对象添加点击事件即可->只需要实现ActionListener接口即可
        go.addActionListener(this);
        /* addActionListener方法在AbstractButton类里而且addActionListener方法非私有即能够继承,
           JButton类继承了AbstractButton类,所以JButton类的对象可以调用addActionListener方法,
           ->go对象属于JButton类,所以go对象可以调用addActionListener方法,
           调用addActionListener方法可以添加点击事件,调用addActionListener方法后
           需要传入参数this表示点击go对象后就会执行this即本类里所对应的方法即被重写的actionPerformed方法,
           因为addActionListener方法的形参是接口ActionListener,该接口有一个抽象方法actionPerformed,
           所以调用addActionListener方法就需要重写ActionListener接口里的actionPerformed方法,
           在addActionListener方法的形参中传入this,就表示执行ActionListener接口中重写的actionPerformed方法
           (传this给addActionListener方法的原因:调用addActionListener方法意味着要操作addActionListener方法的形参,
           addActionListener方法的形参是接口ActionListener,接口ActionListener里有一个抽象方法actionPerformed,
           此时就需要在本类中重写抽象方法actionPerformed,方法actionPerformed最终就在本类中,
           而且要用到actionPerformed方法,由于actionPerformed方法在本类中,所以传this即可调用)
        */
        this.getContentPane().add(go);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        //1.获取被点击的按钮对象
        Object obj = e.getSource();
        //2.判断
        if(obj==go){ //意味着形参e如果是go对象即点击了go就会执行if里的打印语句
            System.out.println("go按钮被点击了");
        }
    }

}
使用方法引用改写后:引用本类的成员方法

其实很简单,LoginJFrame类所实现的ActionListener接口可以不实现了,原因如下:

initView方法中go对象调用了addActionListener方法,addActionListener方法的形参是接口ActionListener:

虽然ActionListener接口没有标记函数式接口,但接口ActionListener本身是一个接口,里面也只有一个抽象方法,所以ActionListener接口也是函数式接口,因此可以在addActionListener方法的参数里使用方法引用:

ActionListener接口里有一个抽象方法actionPerformed,在LoginJFrame类里重写了actionPerformed方法:

在addActionListener方法的形参里可以使用方法引用,而且该形参此时用到了actionPerformed方法,为了使用方法引用,就要找一个方法,该方法的形参类型是ActionEvent,该方法的返回值是void,方法的作用就是获取被点击的按钮对象后进行判断,此时发现Java没有提供,之前也没有写过,所以可以自己写:将要写的方法命名为method,返回值为void,形参类型为ActionEvent,作用和actionPerformed方法一致

此时在initView方法中,go对象调用了addActionListener方法处就可以使用method方法实现方法引用,method方法属于本类方法,使用本类方法的方法引用格式为this::方法名

此时表示点击go按钮后就会执行this本类里的method方法

->提高了代码阅读性,也提高了效率,比如此时需要多个按钮,就可以针对每一个按钮各自写一个方法

->也提高了维护性,比如按钮代码出bug了,其他有关点击的方法的逻辑就不需要看了,直接看对应的方法即可

->对于method方法,还可以继续优化,此时规定了点击go后输出打印,因此也就不需要判断了,当go调用addActionListener方法,就直接调用method方法进行打印,因此可以优化掉method方法里的if语句:

测试类:

package com.itheima.a02game;

public class App {
    public static void main(String[] args) {
        new LoginJFrame();
    }
}

LoginJFrame类:

package com.itheima.a02game;

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

public class LoginJFrame extends JFrame {
    /* go对象起初只被定义在initView方法中,但需求中在actionPerformed方法中也需要用到go对象,
       而且在运行时需要initView方法比actionPerformed方法先运行,
       意味着如果actionPerformed方法此时要想用到go对象,就必须从initView方法中获取,此时只能通过形参传递go对象,
       但actionPerformed方法是Java已经写好的,而且是被重写的,Java写actionPerformed方法时形参中没有关于go对象的,
       所以把go对象只定义在initView方法中是无法传递到actionPerformed方法中的,
       此时只能把创建go对象的语句设置为全局变量供全局使用*/
    JButton go = new JButton("imagine\\Go.jpg");

    public LoginJFrame() {
        //设置图标
        setIconImage(Toolkit.getDefaultToolkit().getImage("imagine\\logo.jpg"));

        //设置界面
        initJframe();

        //添加组件
        initView();

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

    //设置界面
    public void initJframe() {
        //设置标题
        this.setTitle("随机点名器");
        //设置大小
        this.setSize(400, 500);
        //设置关闭模式
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //设置窗口无法进行调节
        this.setResizable(false);
        //设置界面居中
        this.setLocationRelativeTo(null);
        //取消内部默认居中设置
        this.setLayout(null);
        //设置背景颜色
        this.getContentPane().setBackground(Color.white);
        //设置置顶
        this.setAlwaysOnTop(true);
    }

    //添加组件
    public void initView() {
        JLabel image = new JLabel(new ImageIcon("imagine\\点名.jpg"));
        image.setBounds(100, 50, 174, 174);
        this.getContentPane().add(image);

        //go对象属于全局变量
        go.setFont(new Font(null, 1, 20));
        go.setBounds(120, 274, 150, 50);
        go.setBackground(Color.WHITE);
        //需求:点击go对象后在控制台要有数据打印->给go对象添加点击事件即可->只需要实现ActionListener接口即可
        go.addActionListener(this::method);
        /*addActionListener方法的形参是接口ActionListener,虽然ActionListener接口没有标记函数式接口,
        但接口ActionListener本身是一个接口,里面也只有一个抽象方法,所以ActionListener接口也是函数式接口,
        所以可以在addActionListener的参数里使用方法引用*/
        this.getContentPane().add(go);
    }

    public void method(ActionEvent e) {
        System.out.println("go按钮被点击了");
    }

}
使用方法引用改写后:引用父类的成员方法

测试类:

package com.itheima.a02game;

public class App {
    public static void main(String[] args) {
        new LoginJFrame();
    }
}

父类MyJFrame:

package com.itheima.a02game;

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

public class MyJFrame extends JFrame { //父类
    //MyJFrame继承JFrame,使得MyJFrame是一个界面
    public void method(ActionEvent e) {
        System.out.println("go按钮被点击了");
    }
}

子类LoginJFrame:

package com.itheima.a02game;

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

public class LoginJFrame extends MyJFrame { //MyJFrame的子类
    /* MyJFrame继承JFrame,使得MyJFrame是一个界面,
       LoginJFrame继承MyJFrame,使得LoginJFrame也是一个界面*/

    /* go对象起初只被定义在initView方法中,但需求中在actionPerformed方法中也需要用到go对象,
       而且在运行时需要initView方法比actionPerformed方法先运行,
       意味着如果actionPerformed方法此时要想用到go对象,就必须从initView方法中获取,此时只能通过形参传递go对象,
       但actionPerformed方法是Java已经写好的,而且是被重写的,Java写actionPerformed方法时形参中没有关于go对象的,
       所以把go对象只定义在initView方法中是无法传递到actionPerformed方法中的,
       此时只能把创建go对象的语句设置为全局变量供全局使用*/
    JButton go = new JButton("imagine\\Go.jpg");

    public LoginJFrame() {
        //设置图标
        setIconImage(Toolkit.getDefaultToolkit().getImage("imagine\\logo.jpg"));

        //设置界面
        initJframe();

        //添加组件
        initView();

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

    //设置界面
    public void initJframe() {
        //设置标题
        this.setTitle("随机点名器");
        //设置大小
        this.setSize(400, 500);
        //设置关闭模式
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //设置窗口无法进行调节
        this.setResizable(false);
        //设置界面居中
        this.setLocationRelativeTo(null);
        //取消内部默认居中设置
        this.setLayout(null);
        //设置背景颜色
        this.getContentPane().setBackground(Color.white);
        //设置置顶
        this.setAlwaysOnTop(true);
    }

    //添加组件
    public void initView() {
        JLabel image = new JLabel(new ImageIcon("imagine\\点名.jpg"));
        image.setBounds(100, 50, 174, 174);
        this.getContentPane().add(image);

        //go对象属于全局变量
        go.setFont(new Font(null, 1, 20));
        go.setBounds(120, 274, 150, 50);
        go.setBackground(Color.WHITE);
        //需求:点击go对象后在控制台要有数据打印->给go对象添加点击事件即可->只需要实现ActionListener接口即可
        go.addActionListener(super::method); //表示引用的是本类的父类的method方法(这里用this::method也可以实现同样的效果,因为继承有一个特性,this::method表示先在本类找method方法,本类找不到自动到父类找method方法,一直向上找,直到找到method方法为止)
        /*addActionListener方法的形参是接口ActionListener,虽然ActionListener接口没有标记函数式接口,
        但接口ActionListener本身是一个接口,里面也只有一个抽象方法,所以ActionListener接口也是函数式接口,
        所以可以在addActionListener的参数里使用方法引用*/
        this.getContentPane().add(go);
    }



}

initView方法中go对象调用addActionListener方法处用到了方法引用。


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值