Skip to main content
 首页 » 编程设计

java 双冒号操作

2022年07月19日127lyj

java 8 双冒号操作

本文我们讨论java 8 中的双冒号(::)操作以及其使用场景。

从lambda表达式到双冒号(::)操作

我们知道使用lambda表达式可以让代码非常简洁。举例,创建比较器,使用下面语法:

Comparator c = (Computer c1, Computer c2) -> c1.getAge().compareTo(c2.getAge()); 

使用类型推断,可以简写为:

Comparator c = (c1, c2) -> c1.getAge().compareTo(c2.getAge()); 

为了使上面代码更可读,我们使用下面语法:

Comparator c = Comparator.comparing(Computer::getAge); 

我们使用::操作简化lambda表达式调用特定方法,让我们的代码更有表现力。

了解原理

简单地说,当我们使用方法引用时,目标引用在::之前,方法名称在其值后,举例:

Computer::getAge; 

上面方法引用标识调用Computer类的getAge方法。也可以Function一起使用:

Function<Computer, Integer> getAge = Computer::getAge; 
Integer computerAge = getAge.apply(c1); 

我们使用函数引用,然后给其正确的参数执行。

方法引用

我们可以在多个地方使用::操作符。

静态方法

下面示例调用静态工具方法:

List inventory = Arrays.asList( 
  new Computer( 2015, "white", 35), new Computer(2009, "black", 65)); 
inventory.forEach(ComputerUtils::repair); 

现有对象的实例方法

下面看有趣的应用场景——调用现有实例对象方法。我们使用System.out变量——PrintStream类型对象,有print方法:

Computer c1 = new Computer(2015, "white"); 
Computer c2 = new Computer(2009, "black"); 
Computer c3 = new Computer(2014, "black"); 
Arrays.asList(c1, c2, c3).forEach(System.out::print); 

特定类型的任意对象的实例方法

Computer c1 = new Computer(2015, "white", 100); 
Computer c2 = new MacbookPro(2009, "black", 100); 
List inventory = Arrays.asList(c1, c2); 
inventory.forEach(Computer::turnOnPc); 

上面代码没有在特定实例引用turnOnPc方法,而是在类自身。第四行代码将在inventory中每个实例上调用turnOnPc方法。也就是说——调用Computer实例c1的turnOnPc方法,然后调用Computer实例c2的turnOnPc方法.

特定对象超类方法

假设在Computer超类中有下面方法:

public Double calculateValue(Double initialValue) { 
    return initialValue/1.50; 
} 

MacbookPro 子类定义方法:

@Override 
public Double calculateValue(Double initialValue){ 
    Function<Double, Double> function = super::calculateValue; 
    Double pcValue = function.apply(initialValue); 
    return pcValue + (initialValue/10) ; 
} 

在MacbookPro 实例上调用calculateValue方法:

macbookPro.calculateValue(999.99); 

也产生对Computer父类的calculateValue方法的调用。

构造器应用

创建新的实例

引用构造器实例化对象可以简化为:

@FunctionalInterface 
public interface InterfaceComputer { 
    Computer create(); 
} 
  
InterfaceComputer c = Computer::new; 
Computer computer = c.create(); 

如何构造器有两个参数:

BiFunction<Integer, String, Computer> c4Function = Computer::new;  
Computer c4 = c4Function.apply(2013, "white"); 

如果有三个或更多参数,需要定义新的函数接口:

@FunctionalInterface 
interface TriFunction<A, B, C, R> {  
    R apply(A a, B b, C c);  
    default <V> TriFunction<A, B, C, V> andThen( Function<? super R, ? extends V> after) {  
        Objects.requireNonNull(after);  
        return (A a, B b, C c) -> after.apply(apply(a, b, c));  
    }  
} 

初始化对象代码:

TriFunction <Integer, String, Integer, Computer> c6Function = Computer::new; 
Computer c3 = c6Function.apply(2008, "black", 90); 

创建数组

最后,我们看看如何创建5个Computer对象数组:

Function <Integer, Computer[]> computerCreator = Computer[]::new; 
Computer[] computerArray = computerCreator.apply(5); 

总结

java8 引入双冒号操作,在一些场景中非常有用,特别在stream的连接操作中。通过理解函数式接口可以更好地理解其原理。


本文参考链接:https://blog.csdn.net/neweastsun/article/details/82937659
阅读延展