未知对象的调用方法
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 你想调用的方法?
我有两个 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 你想调用的方法?