简体   繁体   English

在java中正确使用的实例

[英]Instanceof correct usage in java

Instance of is considered to be a bad practice in java. 实例被认为是java中的一种不好的做法。 But I don't know how to avoid it in such situation. 但我不知道在这种情况下如何避免它。 Let's say I have an abstract class ArtObject and there are some subclasses such as Painting , Engraving , etc. And I have a class Museum which contains an ArrayList of ArtObject s. 假设我有一个抽象类ArtObject并且有一些子类,如PaintingEngraving等。我有一个类Museum ,其中包含ArtObjectArrayList

For example, I'd like to print information about all Painting objects in the Museum . 例如,我想打印Museum所有Painting对象的信息。

Should I do something like this?: 我应该这样做吗?:

public void printAllPaintingsInfo() {
    for (int i = 0; i < artObjects.size(); i++) {
        if (artObjects.get(i) instanceof Painting) {
            System.out.println(artObjects.get(i));
        }
    }
}

Or a better approach exists? 还是存在更好的方法?

You can add a method isPainting() to ArtObject which is return false; 您可以添加一个方法isPainting()ArtObject这是return false; by default and override to return true; 默认情况下,覆盖return true; in Painting. 在绘画中。

Then you can write 然后你就可以写了

public void printAllPaintingsInfo() {
    for (ArtObject ao : artObjects)
        if (ao.isPainting())
            System.out.println(ao);
}

Or in Java 8 you can write 或者在Java 8中你可以写

artObjects.filter(ArtObject::isPainting).forEach(System.out::println);

As with many language features that are usually frowned upon, it is often tempting to cheat around using them by implementing a mechanism that somehow “fakes” them. 正如通常不赞成的许多语言特征一样,通常会通过实施某种“伪造”它们的机制来欺骗使用它们。 But this doesn't make anything better. 但这并没有让事情变得更好。 If you mean to test the type of an object, then instanceof is just the right thing to use. 如果你的意思是测试一个对象的类型,然后instanceof只是使用了正确的事情。 If you want to avoid it, you'll need to find a way that does not require you to find out the (dynamic) type of an object. 如果你想避免它,你需要找到一种方法, 不需要你找出一个对象的(动态)类型。

One popular pattern to avoid run-time type inspection is using the visitor pattern which effectively has the overload resolution and dynamic dispatch machinery do the dirty work. 避免运行时类型检查的一种流行模式是使用访问者模式,其有效地具有重载分辨率并且动态调度机制执行脏工作。 If your real-world problem is as simple as the museum example you've posted, then this is clearly overkill. 如果您的真实问题与您发布的博物馆示例一样简单,那么这显然是过度的。 Anyway, here is how it could be done. 无论如何,这是如何做到的。

For the sake of this example, assume we have Painting s and Drawings , both derived from ArtObject . 为了这个例子,假设我们有PaintingDrawings ,都是从ArtObject派生的。 We define an abstract visitor type that has a method for each ArtObject . 我们定义了一个抽象的访问者类型,它具有每个ArtObject的方法。 Alternatively, you can only specialize for a few and have a fallback “visit anything” method. 或者,您只能专注于少数几个并具有后备“访问任何东西”的方法。

public abstract class ArtVisitor {

    public void visit(final Painting painting) {
    }

    public void visit(final Drawing drawing) {
    }

    // public void visit(final ArtObject anything) {
    // }
}

I have used an abstract class instead of an interface so I can have empty default implementations of the visit methods. 我使用了抽象类而不是接口,因此我可以使用visit方法的空默认实现。

In the ArtObject interface (or abstract class, for that matter) we need to add an abstract takeVisitor method. 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());
    }
}

Unfortunately, we cannot implement ArtObject.takeVisitor since the this pointer would have static type ArtVisitor , which would require us to do type introspection which is just what we wanted to avoid. 不幸的是,我们无法实现 ArtObject.takeVisitor因为this指针将具有静态类型ArtVisitor ,这将要求我们进行类型内省,这正是我们想要避免的。 Instead, we must – and this is the ugliest thing about the visitor pattern – override it in each of our classes. 相反,我们必须 - 这是关于访问者模式的最丑陋的事情 - 在我们的每个类中覆盖它。

(If we had added the fallback ArtVisitor.visit(ArtObject) method, we could have implemented a generic takeVisitor method but we'd still have to override it in our derived classes to make overload resolution based on the type of the this pointer work. In most cases, this would add more confusion (and potential bugs) than benefits.) (如果我们添加了后备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);
    }
}

Now we can build the museum in a straight-forward manner. 现在我们可以直接建造博物馆。

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();
    }
}

The output of the above program is: 上述程序的输出是:

All ArtObjects:

Mona Lisa (Painting)
Madame Palmyre with Her Dog (Drawing)
The Night Watch (Painting)


All Paintings:

Mona Lisa (Painting)
The Night Watch (Painting)

One possible alternative is to keep a type in all ArtObjects 一种可能的替代方法是在所有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));
        }
    }
}

Another might be to keep a separate Collection for all Painting objects in addition to the artObjects collection. 另一种可能是保持独立的Collection所有的Painting ,除了对象artObjects集合。

I don't think there is any problems using instance of. 我不认为使用实例有任何问题。 But, if you still don't want to use it, you can create a method indicating the type of ArtObject (eg Type.PAINTING, Type.ENGRAVING). 但是,如果您仍然不想使用它,则可以创建一个指示ArtObject类型的方法(例如Type.PAINTING,Type.ENGRAVING)。

I usually define predicates in the base class, or add an explicit subclass name as an instance variable. 我通常在基类中定义谓词,或者添加一个显式子类名作为实例变量。

class ArtObject {

boolean isPainting() { return(false): } boolean isEngraving() { return(false); boolean isPainting(){return(false):} boolean isEngraving(){return(false); } } }}

then in the subclasses, define these methods as true Alternatively 然后在子类中,将这些方法定义为true

typedef enum typeOfArt { generic, painting, engraving };

class ArtObject { typeOfArt type = TypeOfArt.generic; class ArtObject {typeOfArt type = TypeOfArt.generic; } }

depending on the complexity of your model, you might avoid creating subclasses entirely if the clutter caused by switch(type) isn't too great. 根据模型的复杂性,如果switch(类型)引起的混乱不是太大,您可能会完全避免创建子类。

What if we do something like this? 如果我们这样做会怎么样?

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