对 Streams 使用方法引用

使用像对象一样使用方法

  • 使用对象
    我们可以使用对象的引用,或者通过创建新的对象
List list = new ArrayList();
store(new ArrayList());
  • 使用方法
    我们只在另一个方法中使用对象的方法,仍然必须将完整对象作为参数传递。将方法作为参数传递不是更实用吗
    使用lambda 表达式,可以做类似的事情。我们可以使用方法,因为方法引用是只执行 lambda 表达式的一个简写语法。

方法引用

Object :: methodName
  • 使用 lambda 表达式,而不是使用匿名类。但有时,lambda 表达式实际上只是对某个方法的调用
Consumer<String> c = s -> System.out.println(s);

为了使代码更清晰,可以将 lambda 表达式转换为方法引用

Consumer<String> c = System.out::println;
  • 格式
    包含该方法的对象(或类)放在:: 操作符之前,并将方法名放在没有参数的方法之后
  • 限制
    方法引用不能用于任何方法。它们只能用于替换单方法 lambda 表达式。
    要使用方法引用,首先需要一个带有一个方法的 lambda 表达式。要使用 lambda 表达式,首先需要一个函数接口,一个只有一个抽象方法的接口。

方法引用类型

1.引用静态static方法
2.引用特定类型的对象实例方法
3.引用已有对象的实例方法
4.引用构造器方法

静态方法

  • lambda形式
(args) -> Class.staticMethod(args)
  • 方法引用形式
Class::staticMethod

1.使用: : 运算符,并且不向方法引用传递参数
2.不必将参数传递给方法引用。但是,参数的处理取决于方法引用的类型
3.只要可以传递一个只调用静态方法的 lambda 表达式,我们就可以使用方法引用

class Numbers {
    public static boolean isMoreThanFifty(int n1, int n2) {
        return (n1 + n2) > 50;
    }
    public static List<Integer> findNumbers(
            List<Integer> l, BiPredicate<Integer, Integer> p) {
        List<Integer> newList = new ArrayList<>();
        for(Integer i : l) {
            if(p.test(i, i + 10)) {
                newList.add(i);
            }
        }
        return newList;
    }
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(12,5,45,18,33,24,40);
// Using an anonymous class
        List<Integer> numbers = findNumbers(list, new BiPredicate<Integer, Integer>() {
            @Override
            public boolean test(Integer i1, Integer i2) {
                return Numbers.isMoreThanFifty(i1, i2);
            }
        });
// Using a lambda expression
        List<Integer> numbers1 = findNumbers(list, (i1, i2) -> Numbers.isMoreThanFifty(i1, i2));
// Using a method reference
        List<Integer> numbers2 = findNumbers(list, Numbers::isMoreThanFifty);
        System.out.println(numbers+""+numbers1+""+numbers2);
    }
}

上述代码分别使用匿名内部类,lambda表达式和方法引用其输出都是一样的
image.png

特定类型对象的实例方法

传递一个对象的实例,其中一个方法使用一些可选参数执行

  • lambda形式
(obj, args) -> obj.instanceMethod(args)
ObjectType::instanceMethod

1.在方法引用中,我们不使用实例本身。我们使用它的类型。
2.lambda 表达式的另一个参数(如果有的话)并没有在方法引用中使用,但是它像静态方法一样被传递到幕后

方法没有入参示例

class Shipment {
    static Double i = 0.0d;
    public double calculateWeight() {
        double weight = i++;
        // Calculate weight
        return weight;
    }
}
class C {
    public static List<Double> calculateOnShipments(
            List<Shipment> l, Function<Shipment, Double> f) {
        List<Double> results = new ArrayList<>();
        for (Shipment s : l) {
            results.add(f.apply(s));
        }
        return results;
    }
    public static void main(String[] args) {
        List<Shipment> l = new ArrayList<Shipment>();
        l.add(new Shipment());
        l.add(new Shipment());
// Using an anonymous class
        List<Double> doubles = calculateOnShipments(l, new Function<Shipment, Double>() {
            @Override
            public Double apply(Shipment s) { // The object
                return s.calculateWeight(); // The method
            }
        });
// Using a lambda expression
        List<Double> doubles1 = calculateOnShipments(l, s -> s.calculateWeight());
// Using a method reference
        List<Double> doubles2 = calculateOnShipments(l, Shipment::calculateWeight);
        System.out.println(doubles);
        System.out.println(doubles1);
        System.out.println(doubles2);
    }
}

其结果不同,但计算逻辑是相同的。不管是匿名内部类还是lambda或方法引用,都是先使用calculateOnShipments方法遍历shipment集合里面的元素,后使用重写后的Function接口使得static先赋值后再自增1,返回Double元素
image.png

方法有入参实例

JDK自带的Function类型函数式接口含有2个入参或1个入参类型,没有3个入参类型,自定义一个3个入参函数式接口

interface TriFunction<T, U, V, R> {
    R apply(T t, U u, V v);
}

可以使用匿名类或者lambda表达式或者方法引用,甚至隐藏其实现,封装在另一个方法中

class Sum {
    Integer doSum(String s1, String s2) {
        return Integer.parseInt(s1) + Integer.parseInt(s2);
    }

    public static void main(String[] args) {
        TriFunction<Sum, String, String, Integer> anon =
                new TriFunction<Sum, String, String, Integer>() {
                    @Override
                    public Integer apply(Sum s, String arg1, String arg2) {
                        return s.doSum(arg1, arg2);
                    }
                };
        System.out.println(anon.apply(new Sum(), "1", "4"));

        TriFunction<Sum, String, String, Integer> lambda =
                (Sum s, String arg1, String arg2) -> s.doSum(arg1, arg2);
        System.out.println(lambda.apply(new Sum(), "1", "4"));
        TriFunction<Sum, String, String, Integer> mRef = Sum::doSum;
        System.out.println(mRef.apply(new Sum(), "1", "4"));
        System.out.println(integer(new Sum(), "1", "4"));
    }

    static Integer integer(Sum sum, String s, String i) {
        TriFunction<Sum, String, String, Integer> anon =
                new TriFunction<Sum, String, String, Integer>() {
                    @Override
                    public Integer apply(Sum s, String arg1, String arg2) {
                        return s.doSum(arg1, arg2);
                    }
                };
        return anon.apply(sum, "1", "4");
    }
}

image.png

  • 参数解释
    TriFunction < Sum,String,String,Integer >
    1.TriFunction第一个类型参数包含要执行的方法的对象类型,当然内部也可以new一个固定的,但是不通用了,函数式接口应该面向任何类或者对象
    2.第二个类型参数是方法入参的第一个参数的类型
    3.第三个类型参数是方法入参的第二个参数的类型
    4.第四个参数类型是要执行的方法的返回类型。
  • lambda版本
(Sum s, String arg1, String arg2) -> s.doSum(arg1, arg2)
  • 方法引用
Sum::doSum

看起来很怪,但非常简洁🤣

现有对象的实例方法

  • lambda形式
(args) -> obj.instanceMethod(args)
obj::instanceMethod
class Car {
     private int id;
     private String color;
     // More properties
     // And getter and setters
}
class Mechanic {
     public void fix(Car c) {
         System.out.println("Fixing car " + c.getId());
     }
}

image.png

构造函数

  • lambda形式
(args) -> new ClassName(args)
ClassName::new

lambda 表达式唯一要做的就是创建一个新对象,因此我们只需用关键字 new 引用类的一个构造函数。与其他情况一样,参数(如果有的话)没有传递到方法引用中
一般与 java.util.function包的两个(或三个)接口使用此语法

无参构造,使用Supplier

// Using an anonymous class
Supplier<List<String>> s = new Supplier() {
     public List<String> get() {
         return new ArrayList<String>();
     }
};
List<String> l = s.get();

// Using a lambda expression
Supplier<List<String>> s = () -> new ArrayList<String>();
List<String> l = s.get();

// Using a method reference
Supplier<List<String>> s = ArrayList::new;
List<String> l = s.get();

image.png

构造函数含有1个入参,使用Function

// Using a anonymous class
Function<String, Integer> f =
     new Function<String, Integer>() {
         public Integer apply(String s) {
             return new Integer(s);
         }
};
Integer i = f.apply("100");

// Using a lambda expression
Function<String, Integer> f = s -> new Integer(s);
Integer i = f.apply("100");

// Using a method reference
Function<String, Integer> f = Integer::new;
Integer i = f.apply("100");

image.png

构造函数含有2个入参,使用Function

// Using a anonymous class
BiFunction<String, String, Locale> f = new BiFunction<String, String, Locale>() {
     public Locale apply(String lang, String country) {
         return new Locale(lang, country);
     }
};
Locale loc = f.apply("en","UK");

// Using a lambda expression
BiFunction<String, String, Locale> f = (lang, country) -> new Locale(lang, country);
Locale loc = f.apply("en","UK");

// Using a method reference
BiFunction<String, String, Locale> f = Locale::new;
Locale loc = f.apply("en","UK");

image.png

三个及以上入参,使用自定义接口

可以自定义Function类型的函数式接口实现

引用构造函数与引用静态方法非常相似。区别在于构造函数“方法名”是new

总结

1.方法引用是只执行一个方法的lambda表达式的简写语法。
2.如果方法引用能够使代码更加清晰,那么就使用它
3.方法引用有四种类型:
对静态方法的方法引用
对特定类型对象的实例方法的方法引用
对现有对象的实例方法的方法引用
对构造函数的方法引用


这个家伙很懒,啥也没有留下😋