简体   繁体   English

Java:为什么我的List包含不同的类型? (过滤列表)

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

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 . 我想,如果我使用像List<Thing> things = new ArrayList<>()类的List<Thing> things = new ArrayList<>() ,那么这个列表中的所有项都是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 接口Thing

public interface Thing {
  String getType();

  String getName();
}

A class ObjectA 一个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 一个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 main方法

  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> . 所以我假设我的List<Thing>ObjectA实体和ObjectB实体。

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 (eg no good model design). 另外一个问题:我已经开始使用instanceof过滤这个List<Thing> ,但我已经阅读了instanceof和cast是不好的做法(例如没有好的模型设计)。 Is the are "good" way to filter this List for all Types of ObjectA to perform only on these objects some operations? 对于所有类型的ObjectA ,过滤此List的“好”方法是仅对这些对象执行某些操作吗?

You should avoid instanceof check in your additional question example. 您应该避免在其他问题示例中检查instanceof。 When you work with List items, it should be sufficient to have interface methods available. 使用List项时,接口方法可用就足够了。 If you need to do something with only ObjectA or ObjectB, I suggest to use another List with only ObjectA or ObjectB. 如果你只需要用ObjectA或ObjectB做一些事情,我建议使用另一个只有ObjectA或ObjectB的List。 For example you can define different methods to do Thing specific job and ObjectB specific job: 例如,您可以定义不同的方法来执行特定作业和特定于ObjectB的作业:

public void processThings(List<Thing> things) {

        for(final Thing thing : things) {

            // we work only with methods that provided by interface Thing
            System.out.println(thing.getType());
            System.out.println(thing.getName());

        }
    }

public void processObjectsB(List<ObjectB> objectsB) {

        // here we do some specific things with only B objects, 
        // assuming class ObjectB has an additional method doSomeSpecificB()
        for(final ObjectB objectB : objectsB) {

            objectB.doSomeSpecificB();

        }
    }

I have a garden that contains potatoes, carrots and broccoli. 我有一个花园,里面有土豆,胡萝卜和西兰花。 I have a very strict rule - I won't plant anything in the garden that I can't eat. 我有一个非常严格的规则 - 我不会在花园里种植任何我不能吃的东西。 So no poison ivy here! 所以这里没有毒藤!

So this is a Garden<Edible> - everything I plant in the garden has to be edible. 所以这是一个Garden<Edible> - 我在花园种植的一切都必须食用。

Now class Potato implements Edible means that every potato is edible. 现在, class Potato implements Edible意味着每个马铃薯都可食用。 But it also means that I can plant a potato in my garden. 但这也意味着我可以在我的花园种植马铃薯。 Likewise, class Carrot implements Edible - all carrots are edible, and I'm allowed to plant carrots. 同样, class Carrot implements Edible - 所有胡萝卜都可以食用,我可以种植胡萝卜。

It's a dark night, and I'm hungry. 这是一个漆黑的夜晚,我很饿。 I go out to my garden and put my hand on something in the garden. 我出去到花园,把手放在花园里的东西上。 I can't see what it is, but I know that everything in my garden is edible. 我看不出它是什么,但我知道我花园里的一切都可以食用。 So I pull it out of the garden, and take it inside to cook and eat. 所以我把它拉出花园,然后把它拿进去煮饭吃。 It doesn't matter what I've grabbed - I know it will be something I can eat. 我抓住了什么并不重要 - 我知道这将是我可以吃的东西。

Because this is a Garden<Edible> . 因为这是Garden<Edible> It may or may not contain Potato objects. 它可能包含也可能不包含Potato对象。 It may or may not contain Broccoli objects. 它可能包含也可能不包含Broccoli对象。 It does not contain PoisonIvy objects. 它不包含PoisonIvy对象。

Now, translate that all to your example. 现在,将所有内容翻译成您的示例。 You have class ObjectA implements Thing - which means that every ObjectA is a Thing . 你有class ObjectA implements Thing - 这意味着每个ObjectA都是Thing You have class ObjectB implements Thing - which means that every ObjectB is a Thing . 你有class ObjectB implements Thing - 这意味着每个ObjectB都是Thing And you have a List<Thing> - a List that can contain ObjectA objects, ObjectB objects, and any other object of any class that implements Thing . 你有一个List<Thing> - 一个List ,它可以包含ObjectA对象, ObjectB对象,以及任何implements Thing类的任何其他对象。 What you can't put in it is an object of any class that doesn't implement Thing . 你不能放入它的是任何没有实现Thing类的对象。

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. 基本上,由于ObjectAObjectB实现了Thing ,因此ObjectAObjectB实例可以像Thing对象一样使用。 In your code, you added them to a list that can contain Thing objects. 在您的代码中,您将它们添加到可以包含Thing对象的列表中。

Note how even if those objects are now of (compile time) type Thing , at runtime they still know what they are. 注意即使这些对象现在是(编译时)类型Thing ,在运行时他们仍然知道它们是什么。 When you call toString on them, the respective overridden toString methods in ObjectA and ObjectB will be called. 当您对它们调用toString时,将调用ObjectAObjectB相应的重写的toString方法。 It is as if the Thing "morphs" into ObjectA or ObjectB . 好像Thing “变形”为ObjectAObjectB

Is the are "good" way to filter this List for all Types of ObjectA to perform only on these objects some operations? 对于所有类型的ObjectA来说,过滤此List的“好”方法是仅对这些对象执行某些操作吗?

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? 人们之所以说这是不好的做法是因为如果你想根据对象是ObjectA还是ObjectB做不同的事情,你为什么要让它们实现Thing并制作一个Thing列表来存储它们? You could have just used a List<Object> . 您可能刚刚使用了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. 使用List<Thing>的真正优势在于,当您使用List<Thing>可以避免知道实际对象在哪里。 All you know is that the things inside the list implement Thing and you can call methods declared in Thing . 你所知道的是列表中的内容实现了Thing ,你可以调用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 . 一个用于ObjectA ,一个用于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. 在这种情况下,您当前的代码很好。

things is a List<Thing> . thingsList<Thing> That means that at compile time, Java will ensure that any object that you write to things is a Thing . 这意味着在编译时,Java将确保您写入things任何对象都是 Thing As ObjectA and ObjectB both implement Thing the actual implementation of any member of things can be ObjectA or ObjectB . 由于ObjectAObjectB都实现Thing在实际执行中的任何成员的things可以ObjectAObjectB This is by design and that feature is called polymorphism : object of different classes share a common interface and can be accessed thrrough that interface independently of their actual type. 这是设计的,并且该功能称为多态 :不同类的对象共享一个公共接口,并且可以通过该接口独立于其实际类型进行访问。 For example you could use: 例如,您可以使用:

for(final Thing thing : things) {
    System.stdout.println("Found a " + thing.getType() + " named " + thing.getName());
}

Using instanceof and casting is not necessarily bad practice and can have correct use case. 使用instanceof和cast不一定是不好的做法,并且可以有正确的用例。 But it is often a hint that the hierachy of classes and interfaces has not been properly designed. 但它通常暗示类和接口的层次结构尚未正确设计。 Ideally, if you have to process a Thing , you should not wonder about its actual class: you have a Thing , and using Thing methods should be enough. 理想情况下,如果你必须处理Thing ,你不应该怀疑它的实际类:你有一个Thing ,使用Thing方法就足够了。

In that sense, instanceof is at the same level as reflection is: it is a low level tool that allows to see what is hidden under the hood. 从这个意义上说, instanceof与反射处于同一水平:它是一个低级工具,可以看到隐藏在引擎盖下的东西。 And any time you use it, you should ask you whether polymorphism could not be enough. 任何时候你使用它,你应该问你多态是否不够。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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