Java 中 instanceof 運算符的替代方案
一、概述
instanceof
**是一個運算符,用於將對象的實例與類型進行比較**。它也稱為類型比較運算符。
在本教程中,我們將研究傳統instanceof
方法的不同替代方法。我們可能需要替代方案來改進代碼設計和可讀性。
2. 示例設置
讓我們開發一個簡單的程序Dinosaur
and Species
。該程序將有一個父類和兩個子類,即子類將擴展父類。
首先,讓我們創建父類:
public class Dinosaur {
}
接下來,讓我們創建第一個子類:
public class Anatotitan extends Dinosaur {
String run() {
return "running";
}
}
最後,讓我們創建第二個子類:
public class Euraptor extends Dinosaur {
String flies() {
return "flying";
}
}
Dinosaur
類具有子類共有的其他方法,但為簡單起見,我們將跳過它們。
接下來,讓我們編寫一個方法來創建對象的新實例並調用它們的移動。在返回結果之前,我們將使用instanceof
來檢查我們的新實例類型:
public static void moveDinosaur(Dinosaur dinosaur) {
if (dinosaur instanceof Anatotitan) {
Anatotitan anatotitan = (Anatotitan) dinosaur;
anatotitan.run();
}
else if (dinosaur instanceof Euraptor) {
Euraptor euraptor = (Euraptor) dinosaur;
euraptor.flies();
}
}
在接下來的部分中,我們將應用不同的替代方案。
3. 使用getClass()
getClass()
方法有助於獲取對象的類。在檢查對像是否屬於特定類時,我們可以使用getClass()
作為instanceof
的替代方法。
在我們的示例設置中,讓我們維護父類和子類的結構。然後,讓我們為這種方法編寫一個測試方法。我們將使用getClass()
而不是instanceof
:
public static String moveDinosaurUsingGetClass(Dinosaur dinosaur) {
if (dinosaur.getClass().equals(Anatotitan.class)) {
Anatotitan anatotitan = (Anatotitan) dinosaur;
return anatotitan.run();
} else if (dinosaur.getClass().equals(Euraptor.class)) {
Euraptor euraptor = (Euraptor) dinosaur;
return euraptor.flies();
}
return "";
}
讓我們繼續為這種方法編寫單元測試:
@Test
public void givenADinosaurSpecie_whenUsingGetClass_thenGetMovementOfEuraptor() {
assertEquals("flying", moveDinosaurUsingGetClass(new Euraptor()));
}
這個替代方案維護了我們原來的域對象。什麼變化是使用getClass()
。
4.使用多態
多態性的概念使子類覆蓋父類的方法。我們可以使用這個概念來更改我們的示例設置並改進我們的代碼設計和可讀性。
由於我們知道所有恐龍的移動方式,我們可以通過在父類中引入move()
方法來更改我們的設計:
public class Dinosaur {
String move() {
return "walking";
}
}
接下來,讓我們通過覆蓋move()
方法來修改我們的子類:
public class Anatotitan extends Dinosaur {
@Override
String move() {
return "running";
}
}
public class Euraptor extends Dinosaur {
@Override
String move() {
return "flying";
}
}
現在我們可以在不使用instanceof
方法的情況下引用子類。讓我們編寫一個接受我們的父類作為參數的方法。我們將根據其種類返回我們的恐龍運動:
public static String moveDinosaurUsingPolymorphism(Dinosaur dinosaur) {
return dinosaur.move();
}
讓我們為這種方法編寫一個單元測試:
@Test
public void givenADinosaurSpecie_whenUsingPolymorphism_thenGetMovementOfAnatotitan() {
assertEquals("running", moveDinosaurUsingPolymorphism(new Anatotitan()));
}
如果可能,建議使用此方法更改我們的設計本身。 instanceof
的使用通常表明我們的設計違反了 Liskov 替換原則 (LSP)。
5.使用枚舉
在enum
類型中,變量可以定義為預定義常量的集合。我們可以使用這種方法來改進我們的簡單程序。
首先,讓我們創建一個帶有常量和方法的enum
。常量的方法覆蓋enum
中的abstract
方法:
public enum DinosaurEnum {
Anatotitan {
@Override
public String move() {
return "running";
}
},
Euraptor {
@Override
public String move() {
return "flying";
}
};
abstract String move();
}
enum
常量的行為類似於其他替代方案中使用的子類。
接下來,讓我們修改moveDinosaur()
方法以使用e
num
類型:
public static String moveDinosaurUsingEnum(DinosaurEnum dinosaurEnum) {
return dinosaurEnum.move();
}
最後,讓我們為這種方法編寫一個單元測試:
@Test
public void givenADinosaurSpecie_whenUsingEnum_thenGetMovementOfEuraptor() {
assertEquals("flying", moveDinosaurUsingEnum(DinosaurEnum.Euraptor));
}
這種設計讓我們去掉了父類和子類。在父類的行為比我們的示例設置更多的複雜場景中,不建議使用此方法。
6. 使用訪問者模式
訪問者模式有助於對相似/相關的對象進行操作。它將邏輯從對像類移動到另一個類。
讓我們將這種方法應用到我們的示例設置中。首先,讓我們創建一個帶有方法的接口,並將Visitor
作為參數傳遞。這將有助於檢索我們對象的類型:
public interface Dinosaur {
String move(Visitor visitor);
}
接下來,讓我們使用兩個方法創建一個Visitor
界面。這些方法接受我們的子類作為參數:
public interface Visitor {
String visit(Anatotitan anatotitan);
String visit(Euraptor euraptor);
}
接下來,讓我們的子類實現Dinosaur
接口並覆蓋它的方法。該方法將Visitor
作為參數來檢索我們的對像類型。此方法取代了instanceof
的使用:
public class Anatotitan implements Dinosaur {
public String run() {
return "running";
}
@Override
public String move(Visitor dinoMove) {
return dinoMove.visit(this);
}
}
接下來,讓我們創建一個類來實現我們的Visitor
接口並重寫方法:
public class DinoVisitorImpl implements Visitor {
@Override
public String visit(Anatotitan anatotitan) {
return anatotitan.run();
}
@Override
public String visit(Euraptor euraptor) {
return euraptor.flies();
}
}
最後,讓我們為這種方法編寫一個測試方法:
public static String moveDinosaurUsingVisitorPattern(Dinosaur dinosaur) {
Visitor visitor = new DinoVisitorImpl();
return dinosaur.move(visitor);
}
讓我們為這種方法編寫一個單元測試:
@Test
public void givenADinosaurSpecie_whenUsingVisitorPattern_thenGetMovementOfAnatotitan() {
assertEquals("running", moveDinosaurUsingVisitorPattern(new Anatotitan()));
}
這種方法使用了一個接口。 Visitor
包含我們的程序邏輯。
七、結論
在本文中,我們研究了不同的instanceof
替代方案。 instanceof
方法可能違反 Liskov 替換原則。採用替代方案為我們提供了更好、更可靠的設計。建議使用多態性方法,因為它增加了更多價值。
與往常一樣,完整的源代碼可在 GitHub 上獲得。