簡體   English   中英

在java中正確使用的實例

[英]Instanceof correct usage in java

實例被認為是java中的一種不好的做法。 但我不知道在這種情況下如何避免它。 假設我有一個抽象類ArtObject並且有一些子類,如PaintingEngraving等。我有一個類Museum ,其中包含ArtObjectArrayList

例如,我想打印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只是使用了正確的事情。 如果你想避免它,你需要找到一種方法, 不需要你找出一個對象的(動態)類型。

避免運行時類型檢查的一種流行模式是使用訪問者模式,其有效地具有重載分辨率並且動態調度機制執行臟工作。 如果您的真實問題與您發布的博物館示例一樣簡單,那么這顯然是過度的。 無論如何,這是如何做到的。

為了這個例子,假設我們有PaintingDrawings ,都是從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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM