对 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表达式和方法引用其输出都是一样的
特定类型对象的实例方法
传递一个对象的实例,其中一个方法使用一些可选参数执行
- 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元素
方法有入参实例
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");
}
}
- 参数解释
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());
}
}
构造函数
- 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();
构造函数含有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");
构造函数含有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");
三个及以上入参,使用自定义接口
可以自定义Function类型的函数式接口实现
引用构造函数与引用静态方法非常相似。区别在于构造函数“方法名”是new
总结
1.方法引用是只执行一个方法的lambda表达式的简写语法。
2.如果方法引用能够使代码更加清晰,那么就使用它
3.方法引用有四种类型:
对静态方法的方法引用
对特定类型对象的实例方法的方法引用
对现有对象的实例方法的方法引用
对构造函数的方法引用
Comments | 0 条评论