简体   繁体   English

调用未知对象的方法

[英]Call method of unknown object

I have two ArrayLists - ArrayList1 and ArrayList2. 我有两个ArrayLists - ArrayList1和ArrayList2。 Each of them is filled with objects - Object1 and Object2, respectively. 它们中的每一个都分别填充了对象 - 对象1和对象2。 Both of these objects have method 'getText'. 这两个对象都有方法'getText'。

Object1: Object1:

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

Object2: 对象2:

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

At certain point I would like to loop through each of these lists using the same method (just with different parameter). 在某些时候,我想使用相同的方法(仅使用不同的参数)遍历每个列表。

loopThroughList(1)
loopThroughList(2)

What is the syntax if I want to call a method, but I don't know which object it is going to be? 如果我想调用一个方法,语法是什么,但我不知道它将是哪个对象? This is the code I have so far: 这是我到目前为止的代码:

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

It says Cannot resolve method getText . 它说无法解决方法getText I googled around and found another solution: 我用Google搜索并找到了另一种解决方案:

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

But this gives me NoSuchMethodException error. 但这给了我NoSuchMethodException错误。 Even though the 'getText' method is public. 即使'getText'方法是公开的。

EDIT: To get the correct list, I am calling the method 'getList' of a different object (lists) that returns either ArrayList1 or ArrayList2 (depending on the provided parameter). 编辑:为了获得正确的列表,我调用返回ArrayList1或ArrayList2(取决于提供的参数)的不同对象(列表)的方法'getList'。

class Lists

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

Define an interface for the getText method getText方法定义接口

public interface YourInterface {

    String getText();     

}

Implement the interface on the respective classes 在各个类上实现接口

public class Object1 implements YourInterface {

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

}

public class Object2 implements YourInterface {

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

}

Modify your getList method to return List<YourInterface> 修改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;
}

Declaration for loopThroughList loopThroughList声明

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

Sample usage. 样品用法。

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

This is where interface s come in. 这就是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);
    }
}

If one of your objects is not in your control you can use a Wrapper class. 如果您的某个对象不在您的控件中,则可以使用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);
    }
}

This is awful. 这太糟糕了。 Can you elaborate on what specifically you are trying to do? 你能详细说明你想要做什么吗? Java is strong typed by design, and you are trying to get around it. Java是强大的设计类型,你正试图绕过它。 Why? 为什么? Instead of Object, use the specific class, or interface as previously suggested. 而不是Object,使用前面建议的特定类或接口。 If that's not possible, and you must use lists of Objects, use instanceof and casting eg: 如果那是不可能的,并且您必须使用对象列表,请使用instanceof和cast,例如:

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

Interfaces work great here, but there a couple of other options if you're dealing with legacy code and cannot use interfaces. 接口在这里工作得很好,但是如果你处理遗留代码并且不能使用接口,还有其他几个选项。

First would be to cast the list items into their respective types: 首先是将列表项转换为各自的类型:

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

You can also use reflection to see if the object has a getText method and then invoke it: 您还可以使用反射来查看对象是否具有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");
    }
}

Just to add more to this answer and give you some more to think on this (Will try to do it in a simple, non-formal way). 只是为这个答案添加更多内容并给你更多思考(将尝试以简单,非正式的方式)。 Using interfaces is the proper way of doing such operation. 使用接口是执行此类操作的正确方法。 However, I want to stand on the "bad idea": 但是,我想站在“坏主意”:

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

What you are doing here, is using a mechanism called Reflection : 你在这里做的是使用一种名为Reflection的机制:

Reflection is a feature in the Java programming language. 反射是Java编程语言的一个特性。 It allows an executing Java program to examine or "introspect" upon itself, and manipulate internal properties of the program. 它允许执行的Java程序检查或“内省”自身,并操纵程序的内部属性。 For example, it's possible for a Java class to obtain the names of all its members and display them. 例如,Java类可以获取其所有成员的名称并显示它们。

What you actually attempted, is using that mechanism, to retrieve the method through a Class reflection object instance of your Class (sounds weird, isn't it?). 您实际尝试的是使用该机制,通过Class反射对象实例检索方法(听起来很奇怪,不是吗?)。

From that perspective, you need to think that, if you want to invoke your method, you now have, in a sense, a meta-Class instance to manipulate your objects. 从这个角度来看,你需要认为,如果你想调用你的方法,从某种意义上说,你现在拥有一个元类实例来操纵你的对象。 Think of it like an Object that is one step above your Objects (Similarly to a dream inside a dream, in Inception ). 可以把它想象成一个物体,它比你的物体高出一步(类似于梦中的梦想,在初始中 )。 In that sense, you need to retrieve the method, and then invoke it in a different (meta-like) way: 从这个意义上讲,您需要检索该方法,然后以不同的(类似元)方式调用它:

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

Using that logic, you could possibly iterate through the object list, check if method exists, then invoke your method. 使用该逻辑,您可以遍历对象列表,检查方法是否存在,然后调用您的方法。

This is though a bad, BAD idea. 这虽然是坏, 主意。

Why? 为什么? Well, the answer relies on reflection itself: reflection is directly associated with runtime - ie when the program executes, practically doing all things at runtime, bypassing the compilation world. 好吧,答案依赖于反射本身:反射与运行时直接相关 - 即当程序执行时,实际上在运行时执行所有操作 ,绕过编译世界。

In other words, by doing this, you are bypassing the compilation error mechanism of Java, allowing such errors happen in runtime . 换句话说,通过这样做,您绕过了Java的编译错误机制,允许在运行时发生此类错误。 This can lead to unstable behavior of the program while executing - apart from the performance overhead using Reflection, which will not analyze here. 这可能会导致程序执行时行为不稳定 - 除了使用Reflection的性能开销之外,这里不会进行分析。

Side note: While using reflection will require the usage of Checked Exception handling, it still is not a good idea of doing this - as you practically try to duck tape a bad solution. 旁注:虽然使用反射将需要使用Checked Exception处理,但仍然不是这样做的好主意 - 因为你实际上试图扯掉磁带坏的解决方案。

On the other hand, you can follow the Inheritance mechanism of Java through Classes and Interfaces - define an interface with your method (let's call it Textable ), make sure that your classes implement it, and then use it as your base object in your list declaration (@alexrolea has implemented this in his answer, as also @OldCurmudgeon has). 另一方面,您可以通过类和接口遵循Java的继承机制 - 使用您的方法定义接口(让我们称之为Textable ),确保您的类实现它,然后将其用作列表中的基础对象声明(@alexrolea已经在他的回答中实现了这个,同时@OldCurmudgeon也有)。

This way, your program will still make the method call decision making at Runtime (via a mechanism called late binding ), but you will not bypass the compilation error mechanism of Java. 这样,您的程序仍将在运行时进行方法调用决策(通过称为后期绑定的机制),但您不会绕过Java的编译错误机制。 Think about it: what would happen if you define a Textable implementation without providing the class - a compile error! 想一想:如果在没有提供类的情况下定义Textable实现会发生什么 - 编译错误! And what if you set a non-Textable object into the list of Textable s? 如果你将一个非Textable对象设置到Textable列表中Textable办? Guess what! 你猜怎么了! A compile error again. 再次出现编译错误。 And the list goes on.... 而这样的例子不胜枚举....

In general, avoid using Reflection when you are able to do so. 通常,在能够这样做时避免使用Reflection。 Reflection is useful in some cases that you need to handle your program in such a meta-way and there is no other way of making such things. 在某些需要以这种元方式处理程序的情况下,反射很有用,并且没有其他方法可以制作这样的东西。 This is not the case though. 但事实并非如此。

UPDATE: As suggested by some answers, you can use instanceof to check if you have a specific Class object instance that contains your method, then invoke respectively. 更新:正如一些答案所建议的那样,您可以使用instanceof来检查您是否具有包含您的方法的特定Class对象实例,然后分别进行调用。 While this seems a simple solution, it is bad in terms of scaling: what if you have 1000 different classes that implement the same method you want to call? 虽然这似乎是一个简单的解决方案,但在缩放方面却很糟糕:如果你有1000个不同的类实现你想要调用的相同方法怎么办?

your objects have to implement a common interface. 您的对象必须实现一个通用接口。

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

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

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