未知对象的调用方法

时间:2018-10-05 13:12:31

标签: java class object nosuchmethoderror

我有两个ArrayList-ArrayList1和ArrayList2。它们每个都填充有对象-分别为Object1和Object2。 这两个对象都有方法'getText'。

对象1:

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

Object2:

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'方法是公共的。

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

class Lists

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

6 个答案:

答案 0 :(得分:2)

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方法以返回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));
}

答案 1 :(得分:2)

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

答案 2 :(得分:2)

这太可怕了。您能详细说明您要做什么吗? Java是按设计强类型化的,您正在尝试解决它。为什么?代替Object,使用特定的类或接口,如先前建议的那样。如果不可能,则必须使用对象列表,请使用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());
    }
}

答案 3 :(得分:2)

接口在这里很好用,但是如果您要处理旧代码并且不能使用接口,那么还有其他一些选择。

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

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

答案 4 :(得分:1)

只需在此答案中添加更多内容,并给您更多思考的方法(将尝试以一种简单,非正式的方式进行操作)。使用接口是执行此操作的正确方法。但是,我想站在“坏主意”上:

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

您在这里正在使用一种名为Reflection的机制:

  

Reflection是Java编程语言中的一项功能。它允许   执行Java程序以检查或“理解”自身,以及   操纵程序的内部属性。例如,   Java类可以获取其所有成员的名称,并且   显示它们。

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

从这个角度来看,您需要考虑一下,如果您想调用方法,那么从某种意义上说,您现在已经拥有一个元类实例来操纵您的对象。可以把它想象成比对象高出一步的对象(类似于梦中的梦,在 Inception 中)。从这种意义上讲,您需要检索该方法,然后以另一种(类似于元)的方式调用它:

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

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

尽管这是一个错误的想法。

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

换句话说,通过这样做,您将绕过Java的编译错误机制,从而允许在运行时中发生此类错误。除了使用Reflection的性能开销外,这可能导致程序在执行时的不稳定行为。

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

另一方面,您可以通过类和接口遵循Java的继承机制-用您的方法定义一个接口(我们将其称为Textable),确保您的类实现了它,然后使用它作为列表声明中的基础对象(@alexrolea已在其答案中实现了此功能,@ OldCurmudgeon也已实现了此功能)。

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

通常,请尽可能避免使用反射。在某些情况下,反射是很有用的,因为您需要以这种元方式处理程序,而没有其他方法可以进行此类操作。事实并非如此。

更新:如一些答案所建议,您可以使用instanceof来检查是否有包含您的方法的特定Class对象实例,然后分别进行调用。尽管这似乎是一个简单的解决方案,但是在扩展方面却很糟糕:如果您有1000个实现不同 same 方法的不同类,怎么办?

答案 5 :(得分:0)

您的对象必须实现一个公共接口。

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