Java 8中的方法參考
介紹
到目前為止,添加到Java中最甜的語(yǔ)法糖肯定是Lambda Expressions。
Java是一種冗長(zhǎng)的語(yǔ)言,會(huì)妨礙生產(chǎn)率和可讀性。減少樣板代碼和重復(fù)性代碼一直是Java開發(fā)人員的熱門任務(wù),并且通常追求簡(jiǎn)潔,可讀,簡(jiǎn)潔的代碼。
對(duì)于一些常見任務(wù),Lambda Expressions消除了鍵入繁瑣的樣板代碼的需要,它允許開發(fā)人員在不屬于類的情況下調(diào)用它們并將它們像對(duì)象一樣傳遞。
這些表達(dá)式在Java Streams API和Spring的WebFlux框架中主要用于創(chuàng)建反應(yīng)性,動(dòng)態(tài)應(yīng)用程序。
Java 8中另一個(gè)真正有用的功能是方法引用,它使Lambda Expressions更加簡(jiǎn)潔明了,方法是:在使用Lambda Expression僅用于調(diào)用a時(shí),使用方法名稱調(diào)用(引用)方法。方法。
方法參考
方法引用實(shí)質(zhì)上是縮短的Lambda表達(dá)式,用于調(diào)用方法。
它們包括兩個(gè)部分:
Class::method;
常見的示例是打印出說結(jié)果,例如訂閱發(fā)布者服務(wù)或Java Stream:
someCodeChain.subscribe(System.out::println);
讓我們看一下命令式代碼的示例,然后我們將通過Lambda表達(dá)式轉(zhuǎn)向功能代碼,最后通過方法引用進(jìn)行縮短。
我們將做一個(gè)簡(jiǎn)單的類:
public class Employee {
private int id;
private String name;
private int wage;
private String position;
// Constructor, getters and setters
@Override
public String toString() {
return "Name: " + name + ", Wage: " + wage + ", Position: " + position;
}
public int compareTo(Employee employee) {
if (this.wage <= employee.wage) {
return 1;
} else {
return -1;
}
}
}
如果我們將該類形成一個(gè)集合,例如ArrayList,我們將無法使用實(shí)用程序方法對(duì)其進(jìn)行排序,.sort()因?yàn)樗鼪]有實(shí)現(xiàn)Comparable接口。
我們可以做的是new Comparator在調(diào)用.sort()方法時(shí)為這些對(duì)象定義一個(gè):
Employee emp1 = new Employee(1, "David", 1200, "Developer");
Employee emp2 = new Employee(2, "Tim", 1500, "Developer");
Employee emp3 = new Employee(3, "Martha", 1300, "Developer");
ArrayList<Employee> employeeList = new ArrayList<>();
employeeList.add(emp1);
employeeList.add(emp2);
employeeList.add(emp3);
Collections.sort(employeeList, new Comparator<Employee>() {
public int compare(Employee emp1, Employee emp2) {
return emp1.compareTo(emp2);
}
});
System.out.println(employeeList);
運(yùn)行此代碼將產(chǎn)生:
[Name: Tim, Wage: 1500, Position: Developer, Name: Martha, Wage: 1300, Position: Developer, Name: David, Wage: 1200, Position: Developer]
在此,匿名類(Comparator)正在定義比較條件。通過使用Lambda表達(dá)式,我們可以使其更簡(jiǎn)單,更短:
Collections.sort(employeeList, (e1, e2) -> e1.compareTo(e2));
運(yùn)行這段代碼將產(chǎn)生:
[Name: Tim, Wage: 1500, Position: Developer, Name: Martha, Wage: 1300, Position: Developer, Name: David, Wage: 1200, Position: Developer]
再說一次,由于我們對(duì)Lambda表達(dá)式所做的全部工作都稱為一個(gè)方法,因此我們只能引用該方法:
Collections.sort(employeeList, Employee::compareTo);
這還將產(chǎn)生:
[Name: Tim, Wage: 1500, Position: Developer, Name: Martha, Wage: 1300, Position: Developer, Name: David, Wage: 1200, Position: Developer]
方法參考類型
方法引用可以在兩種不同的情況下使用:
- 靜態(tài)方法:
Class::staticMethodName - 特定對(duì)象的實(shí)例方法:
object::instanceMethodName - 仲裁對(duì)象的實(shí)例方法:
Class::methodName - 構(gòu)造函數(shù)參考:
Class::new
讓我們通過一些簡(jiǎn)單的示例來研究所有這些類型。
靜態(tài)方法參考
您可以static通過簡(jiǎn)單地用方法名稱調(diào)用其包含類來引用類的任何方法。
讓我們用static方法定義一個(gè)類,然后從另一個(gè)類中引用它:
public class ClassA {
public static void raiseToThePowerOfTwo(double num) {
double result = Math.pow(num, 2);
System.out.println(result);
}
}
現(xiàn)在,從另一個(gè)類中,讓我們使用static實(shí)用程序方法:
public class ClassB {
public static void main(String[] args) {
List<Double> integerList = new ArrayList<>();
integerList.add(new Double(5));
integerList.add(new Double(2));
integerList.add(new Double(6));
integerList.add(new Double(1));
integerList.add(new Double(8));
integerList.add(new Double(9));
integerList.forEach(ClassA::raiseToThePowerOfTwo);
}
}
運(yùn)行這段代碼將產(chǎn)生:
25.0
4.0
36.0
1.0
64.0
81.0
有許多Java類提供static可以在此處使用的實(shí)用程序方法。在我們的示例中,我們使用了一種自定義方法,盡管在這種情況下不是一個(gè)非常有用的方法。
特定對(duì)象的實(shí)例方法
通過使用對(duì)象的引用變量引用該方法,可以從特定的實(shí)例化對(duì)象調(diào)用方法。
訂閱我們的新聞
在收件箱中獲取臨時(shí)教程,指南和作業(yè)。從來沒有垃圾郵件。隨時(shí)退訂。
訂閱電子報(bào)
訂閱
這通常通過自定義比較器進(jìn)行說明。我們將使用Employee之前的相同類和相同的列表來突出顯示兩者之間的區(qū)別:
public class Employee {
private int id;
private String name;
private int wage;
private String position;
// Constructor, getters and setters
@Override
public String toString() {
return "Name: " + name + ", Wage: " + wage + ", Position: " + position;
}
public int compareTo(Employee employee) {
if (this.wage <= employee.wage) {
return 1;
} else {
return -1;
}
}
}
現(xiàn)在,讓我們定義一個(gè)CustomComparator:
public class CustomComparator {
public int compareEntities(Employee emp1, Employee emp2) {
return emp1.compareTo(emp2);
}
}
最后,讓我們填充一個(gè)列表并對(duì)其進(jìn)行排序:
Employee emp1 = new Employee(1, "David", 1200, "Developer");
Employee emp2 = new Employee(2, "Tim", 1500, "Developer");
Employee emp3 = new Employee(3, "Martha", 1300, "Developer");
ArrayList<Employee> employeeList = new ArrayList<>();
employeeList.add(emp1);
employeeList.add(emp2);
employeeList.add(emp3);
// Initializing our CustomComparator
CustomComparator customComparator = new CustomComparator();
// Instead of making a call to an arbitrary Employee
// we're now providing an instance and its method
Collections.sort(employeeList, customComparator::compareEntities);
System.out.println(employeeList);
運(yùn)行此代碼還將產(chǎn)生:
[Name: Tim, Wage: 1500, Position: Developer, Name: Martha, Wage: 1300, Position: Developer, Name: David, Wage: 1200, Position: Developer]
主要區(qū)別在于,通過添加了另一層,CustomComparator我們可以添加更多功能進(jìn)行比較并將其從類本身中刪除。諸如此類的類Employee不應(yīng)承受復(fù)雜的比較邏輯的負(fù)擔(dān),這將導(dǎo)致更清晰,更易讀的代碼。
另一方面,有時(shí)我們不希望定義自定義比較器,而引入一個(gè)比較器簡(jiǎn)直太麻煩了。在這種情況下,我們將從特定類型的任意對(duì)象中調(diào)用方法,如下一節(jié)所示。
任意對(duì)象的實(shí)例方法
當(dāng)我們將命令式方法通過Lambda Expressions分解為功能性方法時(shí),該示例已在本文開頭顯示。
不過,從良好的角度來看,由于這種方法的使用頻率很高,因此讓我們來看另一個(gè)示例:
List<Integer> integerList = new ArrayList<>();
integerList.add(new Integer(5));
integerList.add(new Integer(2));
integerList.add(new Integer(6));
integerList.add(new Integer(1));
integerList.add(new Integer(8));
integerList.add(new Integer(9));
// Referencing the non-static compareTo method from the Integer class
Collections.sort(integerList, Integer::compareTo);
// Referencing static method
integerList.forEach(System.out::print);
運(yùn)行這段代碼將產(chǎn)生:
125689
雖然這可能看起來像它一樣的靜態(tài)方法的調(diào)用,它不是。這等效于調(diào)用Lambda表達(dá)式:
Collections.sort(integerList, (Integer a, Integer b) -> a.compareTo(b));
在這里,區(qū)別更加明顯。如果我們要調(diào)用一個(gè)static方法,它將看起來像:
Collections.sort(integerList, (Integer a, Integer b) -> SomeClass.compare(a, b));
引用構(gòu)造函數(shù)
您可以像引用static方法一樣引用類的構(gòu)造函數(shù)。
您可以使用對(duì)構(gòu)造函數(shù)的引用,而不是經(jīng)典的類實(shí)例化:
// Classic instantiation
Employee employee = new Employee();
// Constructor reference
Employee employee2 = Employe::new;
根據(jù)上下文,如果存在多個(gè)構(gòu)造函數(shù),則在引用時(shí)將使用足夠的構(gòu)造函數(shù):
Stream<Employee> stream = names.stream().map(Employee::new);
由于名稱流,如果存在Employee(String name)構(gòu)造函數(shù),則將使用它。
可以使用構(gòu)造函數(shù)引用的另一種方式是,當(dāng)您想要將流映射到數(shù)組中時(shí),同時(shí)保留特定的類型。如果要簡(jiǎn)單地映射它,然后調(diào)用toArray(),則會(huì)得到Objects數(shù)組,而不是您的特定類型。
如果我們嘗試過,請(qǐng)說:
Employee[] employeeArray = employeeList.toArray();
當(dāng)然,由于.toArray()返回Objects數(shù)組,因此會(huì)遇到編譯器錯(cuò)誤。投射它也無濟(jì)于事:
Employee[] employeeArray = (Employee[]) employeeList.toArray();
但是這次,它將是運(yùn)行時(shí)異常- ClassCastException。
我們可以通過以下方法避免這種情況:
// Making a list of employees
List<String> employeeList = Arrays.asList("David", "Scott");
// Mapping a list to Employee objects and returning them as an array
Employee[] employeeArray = employeeList.stream().map(Employee::new).toArray(Employee[]::new);
// Iterating through the array and printing information
for (int i = 0; i < employeeArray.length; i++) {
System.out.println(employeeArray[i].toString());
}
這樣,我們得到輸出:
Name: David, Wage: 0, Position: null
Name: Scott, Wage: 0, Position: null
結(jié)論
方法引用是Lambda表達(dá)式的一種,用于在調(diào)用中簡(jiǎn)單地引用方法。使用它們,編寫代碼可以更加簡(jiǎn)潔和可讀。
Lambda Expressions向Java開發(fā)人員介紹了一種更具功能性的編程方法,該方法允許他們避免為簡(jiǎn)單操作編寫冗長(zhǎng)的代碼。
發(fā)布于 2021-01-09 16:16