【从零开始学习JAVA | 第三十篇】方法引用

目录

前言:

方法引用:

方法引用基本概念:

方法可以被引用的条件:

方法引用的种类:

方法引用的优点:

总结:


前言:

方法引用作为一个重要的知识点,虽然他使用起来很复杂,而且会降低代码的可读性,但是如果用好了方法引用,我们也会获得不错的效率,因此我们在今天将为大家讲解什么是方法引用。

方法引用:

方法引用基本概念:

我们用一张图就很好的解释了什么叫做方法引用,而官方对于方法引用的定义为:

Java中,方法引用是一种简洁的写法,用于直接引用现有方法,而不是调用它们。它提供了一种更简洁、易读的方式来传递方法作为参数或在函数式接口中使用。

总而言之:方法引用就是我们把已经有的方法拿过来,当作函数式接口中的抽象方法的方法体。

我们举一个例子:

假设要对一个式子进行排序,我们用lambda表达式的写法,可以写为:

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

});

而如果此时我们的代码中就已经有一个排序的方法 subraction:

public int subraction (int n1.int n2)
{
    return n2-n1;
}

那么我们就可以直接在排序函数中直接拿过来用:

Arrays.sort(arr,方法所在类名::subtraction);

//用我们已经实现的方法subtraction作为排序的规则

其实这就是方法的引用。但是 不是所有的方法都可以引用的!

方法可以被引用的条件:

在Java中,方法引用要满足以下条件才能被引用:

1. 方法引用必须引用一个已存在的方法。也就是说,被引用的方法必须已经被定义。

2. 方法引用的参数类型和数量必须与函数式接口中的抽象方法的参数类型和数量相匹配。也就是说,被引用的方法的参数列表要与函数式接口中的方法参数列表完全匹配。

3. 方法引用的返回类型必须与函数式接口中的抽象方法的返回类型相匹配。也就是说,被引用的方法的返回类型要与函数式接口中的方法返回类型完全匹配。

4. 方法引用必须在上下文中根据目标类型进行推断。也就是说,编译器必须能够根据方法引用的上下文确定需要引用哪个方法。

需要注意的是,方法引用可以用于函数式接口中的抽象方法,包括Lambda表达式和方法引用在内。函数式接口是只有一个抽象方法的接口,可以使用@FunctionalInterface注解来标记。

方法引用的种类:

1.引用静态方法。

        格式:类名::静态方法

        例子:Integer::parseInt

    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "1", "2", "3", "4", "5");

        //普通方法
        list.stream().map(new Function<String, Integer>() {
            @Override
            public Integer apply(String s) {
                int i = Integer.parseInt(s);
                return i;

            }
        }).forEach(s-> System.out.println(s));

        //方法引用
        list.stream().map(Integer::parseInt).forEach(s-> System.out.println(s));
    }
}

2.引用成员方法。

        格式:对象::成员方法

        1️⃣其他类:   其他类对象:: 方法名

案例:从一组字符串姓名中选出姓张且名字是三个字的人:

创建其他类:

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

引用其他类中的方法:

    public static void main(String[] args) {

        //1.创建集合并添加数据
        ArrayList<String>list= new ArrayList<>();
        Collections.addAll(list,"张无忌","周芷若","张敏","张强","张三丰");

        //普通写法
        list.stream().filter(s->s.startsWith("张")).filter(s->s.length()==3).forEach(s-> System.out.println(s));

        //匿名内部类写法
        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 Stringoperation()::stringjudge).forEach(s-> System.out.println(s));


    }

        2️⃣本类:          this::方法名  (引用处不能是静态方法)

import java.util.function.Consumer;
public class test07 {



        public void doSomething() {
            Consumer<String> consumer = this::printMessage; // 使用this::printMessage引用本类方法
            consumer.accept("Hello, world!");
        }

        public void printMessage(String message) {
            System.out.println(message);
        }

        public static void main(String[] args) {
            test07 obj = new test07();
            obj.doSomething();
        }


}

 TIPS:

        this::方法名  这种形式是不可以在静态main方法中实现的,这是因为main有前缀static,使得main方法变为了静态方法,而静态方法中是没有this指针的,如果想要在静态方法中使用本类方法,就要重新创建一个本类的对象,然后再引用。

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

        //1.创建集合并添加数据
        ArrayList<String>list= new ArrayList<>();
        Collections.addAll(list,"张无忌","周芷若","张敏","张强","张三丰");

        //方法引用
        list.stream().filter(new test06()::stringjudge).forEach(s-> System.out.println(s));


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

        3️⃣父类:        super::方法名 (引用处不能是静态方法)                        

class Parent {
    public void printMessage() {
        System.out.println("Hello from Parent class");
    }
}

class Child extends Parent {
    public void printMessage() {
        // 使用super::printMessage引用父类的方法
        Runnable runnable = super::printMessage;
        runnable.run();
    }

    public static void main(String[] args) {
        Child child = new Child();
        child.printMessage();
    }
}

3.引用构造方法。

        格式:类名:: new

        范例:Student :: new

class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    public void introduce() {
        System.out.println("Hello, my name is " + name);
    }
}

interface PersonFactory {
    Person create(String name);
}

class Example {
    public static void main(String[] args) {
        // 使用构造方法引用创建Person对象
        PersonFactory factory = Person::new;
        Person person = factory.create("Alice");

        person.introduce();
    }
}

4.使用类名引用成员方法

        格式:类名::成员方法

        范例:String::substring

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

        //1.创建集合对象并且添加对象
        ArrayList<String> list = new ArrayList<>();

        Collections.addAll(list, "aaa", "bbb", "ccc");

        //2.变成大写之后再输出
        //匿名内部类写法
        list.stream().map(new Function<String, String>() {
            @Override
            public String apply(String s) {
                return s.toUpperCase();
            }
        }).forEach(s -> System.out.println(s));

        //引用类中的方法
        list.stream().map(String::toUpperCase).forEach(s -> System.out.println(s));
    }

}

而使用类名引用成员方法在某些使用规则上与我们最开始的定义不同:

1.被引用方法的形参必须和抽象方法的第二个形参到最后一个形参保持一致,返回值需要保持一致。

抽象方法形参详解:

第一个参数:表示被引用方法的调用者,决定了可以引用哪些类中的方法                                                              在stream流当中,第一个参数一般都表示流里面的每一个数据。                                                      假设现在流里面的数据是字符串,那么使用这种方式进行方法引用的时候                                        只能使用String这给类中的方法。

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

总而言之:使用类名引用成员方法的时候,并不是所有的类名都可以使用,只可以使用抽象方法中第一个参数对应的类型。

而这也就是这种操作的局限性,它限制了我们可以使用方法的种类!

5.引用数组的构造方法:

        格式:数据类型::new 

        范例:int [] new

        细节:数组的类型需要和流中的数组类型保持一致。

public class test09 {
    public static void main(String[] args) {
        //1.创建集合并添加元素
        ArrayList<Integer> list = new ArrayList<>();

        Collections.addAll(list,1,2,3,4,5,6,7,8,9);

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

        for (Integer integer : arr) {
            System.out.println(integer);
        }
            //方法引用
        Integer[] array = list.stream().toArray(Integer[]::new);
        for (Integer integer : array) {
         System.out.println(integer);
       }

    }
}

方法引用的优点:

方法引用(Method Reference)是Java中函数式编程的一项重要特性,它允许我们通过方法的名称来引用一个已经存在的方法,可以看作是Lambda表达式的简化形式。方法引用的优点包括:

1. 简洁性:方法引用能够将繁琐的Lambda表达式进一步简化,使代码更加简洁、直观。通过引用现有的方法,避免了再次编写大量的重复代码。

2. 可读性:方法引用能够提高代码的可读性和可理解性。通过使用已有方法的名称,可以更直观地表示代码的意图和逻辑。

3. 代码复用:方法引用使得代码重用更加方便。可以将已存在的、已经实现的方法作为引用直接传递,从而减少了重复的方法定义和实现。

4. 维护性和一致性:使用方法引用可以使代码更具有一致性和易于维护。当需要修改方法逻辑时,只需要修改被引用的方法,而无需修改引用该方法的所有地方。

5. 高效性:方法引用本质上是通过对已经存在的方法进行复用来实现的,因此在执行效率上可能会更高一些。

需要注意的是,方法引用并不是适用于所有场景,它有一些使用限制,比如需要满足函数式接口的要求,被引用的方法与函数式接口的抽象方法有着相同的参数列表和返回类型等。

总结:

        方法引用的出现,进一步优化了lambda表达式的代码逻辑,优化了我们代码的效率,是一个不错的武器,用来提高我们代码的复用性,一致性和高效性,我们要掌握好方法引用这一好武器。

如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力!

猜你喜欢

转载自blog.csdn.net/fckbb/article/details/131919970
今日推荐