Java: Why does my List contain different types? (Filtering Lists)

SleepyX667 :

The whole time I thought, if I am using a List like List<Thing> things = new ArrayList<>() all items in this list are of Type Thing. Yesterday i was taught the other way.

I've created the following stuff and wonder why it is like it is.

An Interface Thing

public interface Thing {
  String getType();

  String getName();
}

A class ObjectA

public class ObjectA implements Thing {
  private static final String TYPE = "Object A";
  private String name;

  public ObjectA(String name) {
    this.name = name;
  }

  @Override
  public String toString() {
    final StringBuffer sb = new StringBuffer("ObjectA{");
    sb.append("name='").append(name).append('\'');
    sb.append('}');
    return sb.toString();
  }

  @Override
  public String getType() {
    return TYPE;
  }

  @Override
  public String getName() {
    return name;
  }

  // equals and hashCode + getter and setter
}

A class ObjectB

public class ObjectB implements Thing {
  private static final String TYPE = "Object B";
  private String name;
  private int value1;
  private String value2;
  private boolean value3;

  public ObjectB(String name, int value1, String value2, boolean value3) {
    this.name = name;
    this.value1 = value1;
    this.value2 = value2;
    this.value3 = value3;
  }

  @Override
  public String getType() {
    return TYPE;
  }

  @Override
  public String getName() {
    return name;
  }

  @Override
  public String toString() {
    final StringBuffer sb = new StringBuffer("ObjectB{");
    sb.append("name='").append(name).append('\'');
    sb.append(", value1=").append(value1);
    sb.append(", value2='").append(value2).append('\'');
    sb.append(", value3=").append(value3);
    sb.append('}');
    return sb.toString();
  }

  // equals and hashCode + getter and setter
}

The main method

  public static void main(String[] args) {
    final List<Thing> things = new ArrayList<>();
    final ObjectA objA = new ObjectA("Thing 1");
    final ObjectB objB = new ObjectB("Thing 2", 123, "extra", true);

    things.add(objA);
    things.add(objB);

    // The List doesn't contain Thing entities, it contains ObjectA and ObjectB entities
    System.out.println(things);

    for(final Thing thing : things) {
      if (thing instanceof ObjectA) {
        System.out.println("Found Object A: " + thing);
        final ObjectA object = (ObjectA) thing;
      }
      if (thing instanceof ObjectB) {
        System.out.println("Found Object B: " + thing);
      }
    }
  }

The output of this method is:

[ObjectA{name='Thing 1'}, ObjectB{name='Thing 2', value1=123, value2='extra', value3=true}]

So i assume i've ObjectA entities and ObjectB entities in my List<Thing>.

Question: Can someone provide a link (or some keywords which can be used for searching), which explain this behavior, or can explain it to me?

additional Question: I've started to filter this List<Thing> with instanceof but i have read instanceof and casting are bad practice (e.g. no good model design). Is the are "good" way to filter this List for all Types of ObjectA to perform only on these objects some operations?

Sweeper :

Can someone provide a link (or some keywords which can be used for searching), which explain this behavior, or can explain it to me?

This behaviour is called "polymorphism". Basically, since ObjectA and ObjectB implements Thing, instances of ObjectA and ObjectB can be used like a Thing object. In your code, you added them to a list that can contain Thing objects.

Note how even if those objects are now of (compile time) type Thing, at runtime they still know what they are. When you call toString on them, the respective overridden toString methods in ObjectA and ObjectB will be called. It is as if the Thing "morphs" into ObjectA or ObjectB.

Is the are "good" way to filter this List for all Types of ObjectA to perform only on these objects some operations?

The reason why people say this is bad practice is because if you want to do different things depending whether the object is ObjectA or ObjectB, why did you make them implement Thing and make a list of Thing to store them? You could have just used a List<Object>. The real advantage of using List<Thing> is that you avoid knowing what actual objects are in there when you are working with the list. All you know is that the things inside the list implement Thing and you can call methods declared in Thing.

So if you need to filter the list to separate the two types, you could have just created two lists to store them in the first place. One for ObjectA and one for ObjectB. Obviously, this is not always possible, especially if the list comes from somewhere else (like a external library). In that case, your current code is fine.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=112864&siteId=1