[英]Instanceof correct usage in java
實例被認為是java中的一種不好的做法。 但我不知道在這種情況下如何避免它。 假設我有一個抽象類ArtObject
並且有一些子類,如Painting
, Engraving
等。我有一個類Museum
,其中包含ArtObject
的ArrayList
。
例如,我想打印Museum
所有Painting
對象的信息。
我應該這樣做嗎?:
public void printAllPaintingsInfo() {
for (int i = 0; i < artObjects.size(); i++) {
if (artObjects.get(i) instanceof Painting) {
System.out.println(artObjects.get(i));
}
}
}
還是存在更好的方法?
您可以添加一個方法isPainting()
來ArtObject
這是return false;
默認情況下,覆蓋return true;
在繪畫中。
然后你就可以寫了
public void printAllPaintingsInfo() {
for (ArtObject ao : artObjects)
if (ao.isPainting())
System.out.println(ao);
}
或者在Java 8中你可以寫
artObjects.filter(ArtObject::isPainting).forEach(System.out::println);
正如通常不贊成的許多語言特征一樣,通常會通過實施某種“偽造”它們的機制來欺騙使用它們。 但這並沒有讓事情變得更好。 如果你的意思是測試一個對象的類型,然后instanceof
只是使用了正確的事情。 如果你想避免它,你需要找到一種方法, 不需要你找出一個對象的(動態)類型。
避免運行時類型檢查的一種流行模式是使用訪問者模式,其有效地具有重載分辨率並且動態調度機制執行臟工作。 如果您的真實問題與您發布的博物館示例一樣簡單,那么這顯然是過度的。 無論如何,這是如何做到的。
為了這個例子,假設我們有Painting
和Drawings
,都是從ArtObject
派生的。 我們定義了一個抽象的訪問者類型,它具有每個ArtObject
的方法。 或者,您只能專注於少數幾個並具有后備“訪問任何東西”的方法。
public abstract class ArtVisitor {
public void visit(final Painting painting) {
}
public void visit(final Drawing drawing) {
}
// public void visit(final ArtObject anything) {
// }
}
我使用了抽象類而不是接口,因此我可以使用visit
方法的空默認實現。
在ArtObject
接口(或抽象類,就此而言)中,我們需要添加一個抽象的takeVisitor
方法。
public abstract class ArtObject {
private final String name;
protected ArtObject(final String name) {
this.name = name;
}
public abstract void takeVisitor(ArtVisitor visitor);
@Override
public final String toString() {
return String.format("%s (%s)",
this.name,
this.getClass().getCanonicalName());
}
}
不幸的是,我們無法實現 ArtObject.takeVisitor
因為this
指針將具有靜態類型ArtVisitor
,這將要求我們進行類型內省,這正是我們想要避免的。 相反,我們必須 - 這是關於訪問者模式的最丑陋的事情 - 在我們的每個類中覆蓋它。
(如果我們添加了后備ArtVisitor.visit(ArtObject)
方法,我們可以實現一個通用的takeVisitor
方法,但我們仍然必須在派生類中重寫它,以根據this
指針的類型工作進行重載解析。在大多數情況下,這會增加更多的混淆(和潛在的錯誤),而不是好處。)
public final class Painting extends ArtObject {
public Painting(final String name) {
super(name);
}
@Override
public void takeVisitor(final ArtVisitor visitor) {
visitor.visit(this);
}
public void aMethodOnlyPaintingsHave() {
// whatever...
}
}
public final class Drawing extends ArtObject {
public Drawing(final String name) {
super(name);
}
@Override
public void takeVisitor(final ArtVisitor visitor) {
visitor.visit(this);
}
}
現在我們可以直接建造博物館。
import java.util.List;
import java.util.ArrayList;
public final class Museum {
private final List<ArtObject> artworks;
public Museum() {
this.artworks = new ArrayList<ArtObject>();
artworks.add(new Painting("Mona Lisa"));
artworks.add(new Drawing("Madame Palmyre with Her Dog"));
artworks.add(new Painting("The Night Watch"));
}
public void printAllWorks() {
for (final ArtObject work : this.artworks) {
System.out.println(work);
}
}
public void printAllPaintings() {
final ArtVisitor paintingsPrinter = new ArtVisitor() {
@Override
public void visit(final Painting painting) {
System.out.println(painting);
// Note: We don't need any unsafe downcast here!
painting.aMethodOnlyPaintingsHave();
}
};
for (final ArtObject work : this.artworks) {
work.takeVisitor(paintingsPrinter);
}
}
public static void main(final String[] args) {
final Museum myMuseum = new Museum();
System.out.println("All ArtObjects:\n");
myMuseum.printAllWorks();
System.out.println("\n\nAll Paintings:\n");
myMuseum.printAllPaintings();
}
}
上述程序的輸出是:
All ArtObjects:
Mona Lisa (Painting)
Madame Palmyre with Her Dog (Drawing)
The Night Watch (Painting)
All Paintings:
Mona Lisa (Painting)
The Night Watch (Painting)
一種可能的替代方法是在所有ArtObjects
保留一個類型
// Pseudocode
class ArtObject {
Enum artType = { Painting, Engraving }
}
class Painting implements ArtObject {
Painting() { artType = Painting }
}
public void printAllPaintingsInfo() {
for(int i = 0; i < artObjects.size(); i++) {
if(artObjects.artType == Painting) {
System.out.println(artObjects.get(i));
}
}
}
另一種可能是保持獨立的Collection
所有的Painting
,除了對象artObjects
集合。
我不認為使用實例有任何問題。 但是,如果您仍然不想使用它,則可以創建一個指示ArtObject類型的方法(例如Type.PAINTING,Type.ENGRAVING)。
我通常在基類中定義謂詞,或者添加一個顯式子類名作為實例變量。
class ArtObject {
boolean isPainting(){return(false):} boolean isEngraving(){return(false); }}
然后在子類中,將這些方法定義為true
typedef enum typeOfArt { generic, painting, engraving };
class ArtObject {typeOfArt type = TypeOfArt.generic; }
根據模型的復雜性,如果switch(類型)引起的混亂不是太大,您可能會完全避免創建子類。
如果我們這樣做會怎么樣?
public abstract class ArtObject{
//Classes extending this class will need to override and implement this method
abstract public Type getType();
}
public class Painting extends ArtObject {
/*
Other code probably
*/
@Override
public Type getType(){
return Type.Painting;
}
}
enum Type {
Painting, Museum;
}
public void printAllPaintingsInfo() {
for (int i = 0; i < artObjects.size(); i++) {
if (artObjects.get(i).getType() == Type.Painting) {
System.out.println(artObjects.get(i));
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.