我有两个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;
}
}
答案 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());
}
}