【问题标题】:Java: Why does my List contain different types? (Filtering Lists)Java:为什么我的 List 包含不同的类型? (过滤列表)
【发布时间】:2018-07-11 07:21:37
【问题描述】:

我一直在想,如果我使用像List<Thing> things = new ArrayList<>() 这样的列表,那么这个列表中的所有项目都是Thing 类型的。昨天我被教导了另一种方式。

我创建了以下内容,想知道为什么会这样。

一个接口Thing

public interface Thing {
  String getType();

  String getName();
}

A类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类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
}

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

这个方法的输出是:

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

所以我假设我的List&lt;Thing&gt; 中有ObjectA 实体和ObjectB 实体。

问题:谁能提供一个链接(或一些可用于搜索的关键字)来解释这种行为,或者可以向我解释一下吗?

其他问题:我已经开始用instanceof 过滤这个List&lt;Thing&gt;,但我读过instanceof 和casting 是不好的做法(例如没有好的模型设计)。为ObjectA 的所有类型过滤此列表以仅对这些对象执行某些操作是“好”的方法吗?

【问题讨论】:

  • ObjectAObjectBThing 的实例 - 这是实际的多态性
  • 您不需要过滤。如果您不想将ObjectAObjectB 放在同一个集合中,请不要将它们放在那里。如果您确实将它们放在那里,请确保可以使用它们而无需关心它们是哪种实现,即只考虑它们Things

标签: java list casting instanceof


【解决方案1】:

您应该避免在您的附加问题示例中进行 instanceof 检查。当您使用列表项时,有可用的接口方法就足够了。如果您只需要使用 ObjectA 或 ObjectB 做某事,我建议您使用另一个仅使用 ObjectA 或 ObjectB 的列表。例如,您可以定义不同的方法来执行特定于 Thing 的作业和特定于 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();

        }
    }

【讨论】:

  • 您能否在答案中添加一些示例源代码?
【解决方案2】:

我有一个种满土豆、胡萝卜和西兰花的花园。我有一个非常严格的规定——我不会在花园里种任何我不能吃的东西。所以这里没有毒藤!

所以这是Garden&lt;Edible&gt; - 我在花园里种的所有东西都必须是可食用的。

现在class Potato implements Edible 意味着每个土豆都可以食用。但这也意味着我可以在我的花园里种土豆。同样,class Carrot implements Edible - 所有的胡萝卜都可以食用,我可以种胡萝卜。

这是一个漆黑的夜晚,我饿了。我走到我的花园里,把手放在花园里的什么东西上。我看不出它是什么,但我知道我花园里的一切都是可以食用的。所以我把它从花园里拉出来,拿进去做饭吃。我抓到了什么并不重要——我知道它会是我可以吃的东西。

因为这是Garden&lt;Edible&gt;。它可能包含也可能不包含Potato 对象。它可能包含也可能不包含Broccoli 对象。它不包含PoisonIvy 对象。

现在,将所有内容转换为您的示例。你有class ObjectA implements Thing - 这意味着每个ObjectA 都是Thing。你有class ObjectB implements Thing - 这意味着每个ObjectB 都是Thing。你有一个List&lt;Thing&gt; - 一个List,它可以包含ObjectA 对象、ObjectB 对象以及implements Thing 的任何类的任何其他对象。您不能放入其中的是实现Thing的任何类的对象。

【讨论】:

  • 很好的例子!但是您可能需要一个可覆盖的cook 方法,因为我认为PotatoBroccoli 之间应该是不同的,而不是Tomato...
【解决方案3】:

谁能提供一个链接(或一些可用于搜索的关键字)来解释这种行为,或者可以向我解释一下吗?

这种行为称为“多态性”。基本上,由于ObjectAObjectB 实现了ThingObjectAObjectB 的实例可以像Thing 对象一样使用。在您的代码中,您将它们添加到可以包含 Thing 对象的列表中。

请注意,即使这些对象现在是(编译时)类型Thing,在运行时它们仍然知道它们是什么。当您在它们上调用toString 时,将调用ObjectAObjectB 中各自覆盖的toString 方法。好像Thing“变形”为ObjectAObjectB

为所有类型的 ObjectA 过滤此列表以仅对这些对象执行某些操作是“好”的方法吗?

人们之所以说这是不好的做法是因为如果你想做不同的事情取决于对象是ObjectA还是ObjectB,你为什么要让它们实现Thing并列出@987654339 @存储它们?您可以只使用List&lt;Object&gt;。使用List&lt;Thing&gt; 的真正优势在于您避免在处理列表时知道其中有哪些实际对象。你只知道列表里面的东西实现了Thing,你可以调用Thing中声明的方法。

因此,如果您需要过滤列表以区分这两种类型,您可以首先创建两个列表来存储它们。一种用于ObjectA,另一种用于ObjectB。显然,这并不总是可行的,特别是如果列表来自其他地方(如外部库)。在这种情况下,您当前的代码很好。

【讨论】:

  • 我对你的句子有一个注释:“注意即使这些对象现在是(编译时)类型的东西......”。如果我在 Idea IntelliJ Thing thing1 = new ObjectB("Thing 2", 123, "extra", true); 中执行以下操作,我可以访问所有方法,例如 thing1.getValue3()。因此,即使在“编译时”,它似乎也不是Thing。这是一个 ObjectB,还是我错过了什么?
  • @SleepyX667 在这种情况下,您无法访问getValue3。你投了吗?
  • '@Sweeper' 对不起。这是我的错。我在 Dropbox 中获得了方法列表(所以我假设我可以访问它们)但是当我按下 Enter 时,IntelliJ 会进行强制转换。
【解决方案4】:

thingsList&lt;Thing&gt;。这意味着在编译时,Java 将确保您写入things 的任何对象 Thing。由于ObjectAObjectB 都实现了Thingthings 的任何成员的实际实现 可以是ObjectAObjectB。这是设计,该特性称为多态性:不同类的对象共享一个公共接口,并且可以通过该接口独立于它们的实际类型进行访问。例如,您可以使用:

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

使用instanceof 和强制转换不一定是不好的做法,并且可以有正确的用例。但这通常暗示着类和接口的层次结构没有被正确设计。理想情况下,如果你必须处理一个Thing,你不应该怀疑它的实际类:你有一个Thing,并且使用Thing 方法就足够了。

从这个意义上说,instanceof 与反射处于同一级别:它是一个低级工具,可以查看隐藏在引擎盖下的内容。并且任何时候你使用它时,你应该问你多态性是否不够。

【讨论】:

    猜你喜欢
    • 2018-07-24
    • 2020-09-11
    • 2021-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多