简体   繁体   中英

Looping over heterogeneous collection

The example is as follows: I have a Box that needs to be filled with some Thing s. I'm interested only in weight of each thing. Also, beside weight, I need to correctly identify the thing that I'm measuring. Each thing type has different Id type. In this case I have toys and fruits, which have ToyId and FruitId respectively. In the end, I need to be able to print thing identifier and thing weight.

Question: Is it somehow possible to access specific methods on ThingId s without using instanceof operator (as in example)?

class Color{}
interface ThingId {}

class FruitId implements ThingId {
    String name;    //"apple", "orange", ...
    FruitId(String name){  this.name = name; }
    String getName(){ return this.name; }
}

class ToyId implements ThingId {
    String shape;   //"square", "circle", ...
    Color color;    //"red", "blue"...
    ToyId(String shape, Color color){ this.shape = shape; this.color = color; }
    String getShape(){ return this.shape; }
    Color getColor(){ return this.color; }
}

class Thing{
    ThingId thingId;
    Integer weight;
    public Thing(ThingId thingId, Integer weight){
        this.thingId = thingId;
        this.weight = weight;
    }
    ThingId getThingId(){ return this.thingId; }
    Integer getWeight(){ return this.weight; }
}

class Box {
    Set<Thing> things = new HashSet<>();

    void addThing(Thing thing){
        this.things.add(thing);
    }

    Collection<Thing> getThings(){
        return this.things;
    }
}

class Program {
    public static void main(String[] args) {
        FruitId appleId = new FruitId("apple");
        Thing apple = new Thing(appleId, 1);
        ToyId cubeId = new ToyId("square", new Color());
        Thing cube = new Thing(cubeId, 22);

        Box box = new Box();
        box.addThing(apple);
        box.addThing(cube);

        for(Thing t : box.getThings()){
            System.out.print("Thing Id is: ");
            if(t.getThingId() instanceof FruitId) { //any other possibility than using instance of?
                process((FruitId)t.getThingId());
            }
            if(t.getThingId() instanceof ToyId){    //any other possibility than using instance of?
                process((ToyId)t.getThingId());
            }
            System.out.println("Weight is : " + t.getWeight());
        }
    }

    static void process(FruitId fruitId){
        System.out.println(fruitId.getName());
    }

    static void process(ToyId toyId){
        System.out.println(toyId.getShape() + toyId.getColor());
    }
}

UPDATE

OK, I think Visitor pattern could be useful here:

class Color{}
interface ThingId {
    void visitThingId(ThingIdVisitor visitor);
}

class FruitId implements ThingId {
    String name;    //"apple", "orange", ...
    FruitId(String name){  this.name = name; }
    String getName(){ return this.name; }

    @Override
    public void visitThingId(ThingIdVisitor visitor) {
        visitor.process(this);
    }
}

class ToyId implements ThingId {
    String shape;   //"square", "circle", ...
    Color color;    //"red", "blue"...
    ToyId(String shape, Color color){ this.shape = shape; this.color = color; }
    String getShape(){ return this.shape; }
    Color getColor(){ return this.color; }

    @Override
    public void visitThingId(ThingIdVisitor visitor) {
        visitor.process(this);
    }
}

class Thing{
    ThingId thingId;
    Integer weight;
    public Thing(ThingId thingId, Integer weight){
        this.thingId = thingId;
        this.weight = weight;
    }
    ThingId getThingId(){ return this.thingId; }
    Integer getWeight(){ return this.weight; }
}

class Box {
    Set<Thing> things = new HashSet<>();

    void addThing(Thing thing){
        this.things.add(thing);
    }

    Collection<Thing> getThings(){
        return this.things;
    }
}

class ThingIdVisitor{
    void process(FruitId fruitId){
        System.out.println(fruitId.getName());
    }

    void process(ToyId toyId){
        System.out.println(toyId.getShape() + toyId.getColor());
    }
}

class Program {
    public static void main(String[] args) {
        FruitId appleId = new FruitId("apple");
        Thing apple = new Thing(appleId, 1);
        ToyId cubeId = new ToyId("square", new Color());
        Thing cube = new Thing(cubeId, 22);

        Box box = new Box();
        box.addThing(apple);
        box.addThing(cube);

        for(Thing t : box.getThings()){
            System.out.print("Thing Id is: ");

            t.getThingId().visitThingId(new ThingIdVisitor());

            System.out.println("Weight is : " + t.getWeight());
        }
    }
}

I don't really get what you're trying to achieve. First of all, I don't get the use of the interface ThingId . Second, I think you're a bit confused about interfaces and inheritance. If I were you, I'd look up polymorphism .

Anyway, I propose you remove the ThingId interface and let the FruitId and ToyId classes extend the Thing class. As your collection only exists of Thing s, and as your Fruit and Toy classes all extend this Thing class and thus implement the getWeight() method, you should not use instanceof anymore.

But please, read up on polymorphism .

your interface ThingId must provide the respective methods that you want to have.

If you simple want to print out information, then you can use like a simple

public String getInformation();

Then the implementations can return the information that is relevant for them and you can simply work with ThingId in your application code.

BTW: As you are storing your Thing s in a HashSet make sure to implement equals and hashCode in all Thing implementations

Also I dont really see, why you need a Thing and a ThingId , as ThingId seems a bit more than a simple id and actually a thing. So for me it seems that ThingId is redundant and all can be achieved by the having different Thing s

Since you are calling the same method process on both the instance types, why not add that method to ThingId interface itself.

By doing so, you could just call:

t.getThingId().process();

Instead of finding the instance type and calling respective methods.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM