从包含不同 类 对象的列表的每个对象中检索实例变量的特定值

Retrieve specific values of instance variable from each object of a list containing Objects of different classes

我有一个列表,其中包含不同 类 的对象,例如 A、B、C、D、E、F、G。每个对象(A、B、C、D、E、F , G) 中有一个实例变量“name”。我想遍历列表并检索存储在每个对象的名称变量中的值。

我试过下面的代码

List<String> str = new ArrayList<String>();
for(Object obj: list_containing_different_objects) {
      str.add(obj.name) or  str.add(obj.getName());
}

但是 obj.name 会给出错误,因为我不知道 list_containing_different_objects 中的确切索引(因为每次都可能不同),其中 A、B、C、D、E、 F 被存储。另外,如果我知道索引,我无法理解 it.Can 有人能帮我解决这个问题吗?

这取决于class是ABCDEFG 有共同的基础 class 或接口。

普通基础类型

如果没有,您可以创建一个如下所示的界面:

interface Nameable {
    String getName();
}

然后让每个 classes 实现这个接口。然后您可以轻松地在每个对象上调用 getName() 方法:

for (Nameable nameable : listContainingDifferentObjects) {
    str.add(nameable.getName());
}

instanceof

否则,如果 classes 不会或不能实现公共接口或扩展基础 class,则 classes 无关,尽管有一个同名字段。

在那种情况下,您最终必须检查每种类型,转换它并使用字段1:

for (Nameable nameable : listContainingDifferentObjects) {
    if (obj instanceof A) {
        str.add(((A) obj).name);
    }
    else if (obj instanceof B) {
        str.add(((B) obj).name);
    }
    // et cetera
}

如果您的 classes 没有实现通用类型,但仍然有一个同名字段,我会重新考虑设计。

反思

vsfDawg 想出了使用反射来动态调用 getName() 方法或访问 name 字段。虽然一个 确实可以 使它工作,但如果可能的话,你应该避免使用它,并且只有在没有其他方法的情况下才使用它。 ➤ More details


1从16版本开始,Java支持模式匹配,简化了检查类型、转换和使用的仪式价值。例如:

for (Object obj : listContainingDifferentObjects) {
    if (obj instanceof A a) {
        str.add(a.name());
    }
    else if (obj instanceof B b) {
        str.add(b.name());
    }
    // et cetera
}

有关详细信息,请参阅 this article

使用反射

假设 classes 不共享包含方法的公共基础 class 或接口,您可以使用反射来调用方法。有很多错误处理可以使其正常工作,但下面的示例将错误处理掉并强制调用者处理它。

public String getName(Object named)
throws NoSuchMethodException, 
       SecurityException,
       IllegalAccessException,
       IllegalArgumentException,
       InvocationTargetException
{
  Method method = named.getClass().getMethod("getName");
  return (String) method.invoke(named);
}

下面遍历一个List of objects并调用上面的方法。由于异常正在渗透,因此此方法必须处理它们。您可以在此处捕获异常,但为了完整起见,我保留了完整列表。

public void printNames(List<Object> namedObjects) {
  for (Object obj : namedObjects) {
    try  {
      System.out.println(getName(obj));
    } catch (NoSuchMethodException |
             SecurityException |
             IllegalAccessException |
             IllegalArgumentException |
             InvocationTargetException e) {
      System.err.println("Failed to invoke getName on object - skipping");
    }
  }
}

我喜欢'MC Emperor's answer about common base class,这是要走的路。 但是,如果您没有那个通用的 class 并且无法更改代码。 可以使用反射来实现所需要的。

下面的代码甚至不需要 getter (getName()),它只需要一个 'name' 字段。

class A {
    public A(String aName) {
        name = aName;
    }

    public String name;
}

class B {
    public B(String aName) {
        name = aName;
    }

    public String name;
}

class C {
    public C(String aName) {
        name = aName;
    }

    public String name;
}

@Test
public void testClasses() {
    List<String> str = new ArrayList<>();
    List<? extends Object> list_containing_different_objects = Lists.list(new A("a"), new B("b"),
            new C("c"), new A("aa"));
    
    for (Object commonObj : list_containing_different_objects) {
        try {
            Field field = commonObj.getClass().getField("name");
            String nameValue = (String) field.get(commonObj);

            str.add(nameValue);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    // print the results
    str.forEach(System.out::println);
}

此测试执行的结果如下所示:

 a
 b
 c
 aa

希望对您有所帮助。