未知对象的调用方法

Call method of unknown object

我有两个 ArrayList - ArrayList1 和 ArrayList2。它们中的每一个都填充有对象——分别是 Object1 和 Object2。 这两个对象都有方法 'getText'.

对象 1:

public String getText() { return "1";}

对象 2:

public String getText() { return "2";}

在某些时候,我想使用相同的方法(只是使用不同的参数)遍历每个列表。

loopThroughList(1)
loopThroughList(2)

如果我想调用一个方法,但我不知道它是哪个对象,语法是什么?这是我到目前为止的代码:

for (Object o : lists.getList(listNumber)) {
    System.out.println(o.getText());
}

它说 无法解析方法 getText。我四处搜索并找到了另一个解决方案:

for (Object o : lists.getList(listNumber)) {
    System.out.println(o.getClass().getMethod("getText"));
}

但这给了我 NoSuchMethodException 错误。尽管 'getText' 方法是 public。

编辑: 为了获得正确的列表,我正在调用另一个对象(列表)的方法 'getList' returns ArrayList1 或 ArrayList2 (取决于提供的参数)。

class Lists

public getList(list) {
    if (list == 1) {
        return ArrayList1;
    }
    else if (list == 2) {
        return ArrayList2;
    }
}

getText 方法定义一个接口

public interface YourInterface {

    String getText();     

}

在相应的 类

上实现接口
public class Object1 implements YourInterface {

    @Override
    public String getText() { 
        return "1";
    }

}

public class Object2 implements YourInterface {

    @Override
    public String getText() { 
        return "2";
    }

}

将您的 getList 方法修改为 return List<YourInterface>

public static List<YourInterface> getList(int list){
    List<YourInterface> result = new ArrayList<>();
    if(list == 1){
        // your initial type
         List<Object1> firstList = new ArrayList<>();
         result.addAll(firstList);
    } else {
        // your initial type
        List<Object2> secondList = new ArrayList<>();
        result.addAll(secondList);
    }
    return result;
}

loopThroughList

的声明
public static void loopThroughList(List<YourInterface> list){
    list.forEach(yourInterface -> System.out.println(yourInterface.getText()));
}

示例用法。

public static void main(String[] args) {
    loopThroughList(getList(1));
    loopThroughList(getList(2));
}

您的对象必须实现通用接口。

interface GetTextable {
    String getText();
}

class One implements GetTextable {
    private final String text;

    public One(final String text) {
        this.text = text;
    }

    public String getText() {
        return this.text;
    }
}

class Two implements GetTextable {
    private final String text;

    public Two(final String text) {
        this.text = text;
    }

    public String getText() {
        return this.text;
    }
}

@Test
public void shouldIterate() throws Exception {
    List<GetTextable> toIterate = Arrays.asList(new One("oneText"), new Two("twoText"));
    for(GetTextable obj: toIterate) {
        System.out.println(obj.getText());
    }
}

这是 interface 发挥作用的地方。

interface HasText {
    public String getText();
}

class Object1 implements HasText {
    @Override
    public String getText() {
        return "1";
    }
}

class Object2 implements HasText {
    @Override
    public String getText() {
        return "2";
    }
}

private void test() {
    List<HasText> list = Arrays.asList(new Object1(), new Object2());
    for (HasText ht : list) {
        System.out.println(ht);
    }
}

如果您的对象之一不在您的控制范围内,您可以使用 Wrapper class。

class Object3DoesNotImplementHasText {
    public String getText() {
        return "3";
    }
}

class Object3Wrapper implements HasText{
    final Object3DoesNotImplementHasText it;

    public Object3Wrapper(Object3DoesNotImplementHasText it) {
        this.it = it;
    }

    @Override
    public String getText() {
        return it.getText();
    }
}

private void test() {
    List<HasText> list = Arrays.asList(new Object1(), new Object2(), new Object3Wrapper(new Object3DoesNotImplementHasText()));
    for (HasText ht : list) {
        System.out.println(ht);
    }
}

这太糟糕了。你能详细说明你具体想做什么吗? Java 是强类型的设计,你正试图绕过它。为什么?使用特定的 class 或先前建议的接口而不是对象。如果那不可能,并且您必须使用对象列表,请使用 instanceof 和强制转换,例如:

for (Object o : lists.getList(listNumber)) {
    if (o instanceof Object1) {
        Object1 o1 = (Object1) o;
        System.out.println(o1.getText());
    } else if (o instanceof Object2) {
        Object2 o2 = (Object2) o;
        System.out.println(o2.getText());
    }
}

接口在这里工作得很好,但如果您正在处理遗留代码并且不能使用接口,还有几个其他选项。

首先是将列表项转换为各自的类型:

for (Object o : lists.getList(listNumber)) {
    if(o instanceof Object1) {
        Object1 o1 = (Object1)o;
        System.out.println(o1.getText());
    }
    else if(o instanceof Object2) {
        Object1 o2 = (Object2)o;
        System.out.println(o2.getText());
    }
    else {
        System.out.println("Unknown class");
    }
}

你也可以使用反射来查看对象是否有getText方法然后调用它:

for (Object o : lists.getList(listNumber)) {
    try {
        System.out.println(o.getClass().getDeclaredMethod("getName").invoke(o));
    }
    catch(Exception e) {
        System.out.println("Object doesn't have getText method");
    }
}

只是为了给这个答案添加更多内容并让您对此有更多思考(将尝试以简单、非正式的方式进行)。使用接口是进行此类操作的正确方法。但是,我想站在"bad idea":

for (Object o : lists.getList(listNumber)) {
    System.out.println(o.getClass().getMethod("getText"));
}

您在这里所做的是使用一种称为 Reflection:

的机制

Reflection is a feature in the Java programming language. It allows an executing Java program to examine or "introspect" upon itself, and manipulate internal properties of the program. For example, it's possible for a Java class to obtain the names of all its members and display them.

您实际尝试的是使用该机制,通过 Class 的 Class 反射对象实例检索方法(听起来很奇怪,不是吗?)。

从这个角度来看,您需要考虑,如果您想调用您的方法,从某种意义上说,您现在拥有一个元Class 实例来操作您的对象。把它想象成一个比你的对象高一级的对象(类似于 Inception 中的梦中梦)。从这个意义上讲,您需要检索该方法,然后以不同的(类似元的)方式调用它:

java.lang.reflect.Method m = o.getClass().getMethod("getText");
m.invoke(o);

使用该逻辑,您可以遍历对象列表,检查方法是否存在,然后调用您的方法。

这虽然是一个糟糕的,糟糕的想法。

为什么?好吧,答案取决于反射本身:反射与 runtime 直接相关——即当程序执行时,实际上在运行时做 所有事情,绕过编译世界。

换句话说,这样做就是绕过了Java的编译错误机制,让这样的错误发生在运行时。这会导致程序在执行时出现不稳定的行为——除了使用反射的性能开销外,这里不再分析。

旁注:虽然使用反射需要使用 Checked Exception 处理,但这样做仍然不是一个好主意 - 因为您实际上是在尝试回避错误的解决方案。

另一方面,您可以通过 Classes 和接口遵循 Java 的继承机制——用您的方法定义一个接口(我们称之为 Textable),使确保你的 classes 实现了它,然后在你的列表声明中将它用作你的基础对象(@alexrolea 在他的回答中实现了这个,@OldCurmudgeon 也实现了)。

这样,您的程序仍然会在运行时做出方法调用决策(通过称为后期绑定的机制),但您不会绕过[的编译错误机制=61=]。想一想:如果您在不提供 class 的情况下定义一个 Textable 实现会发生什么——编译错误!如果您将一个非 Textable 对象设置到 Textable 的列表中怎么办?你猜怎么着!再次编译错误。清单还在继续....

一般来说,尽可能避免使用反射。反射在某些情况下很有用,您需要以这种元方式处理您的程序,并且没有其他方法可以制作这样的东西。但事实并非如此。

更新:正如一些答案所建议的,您可以使用 instanceof 检查您是否有一个包含您的方法的特定 Class 对象实例,然后分别调用。虽然这看起来是一个简单的解决方案,但它在缩放方面很糟糕:如果你有 1000 个 different classes 实现 same 你想调用的方法?