我有3个类A,B和C.他们唯一的共同点是getName()函数,没有别的。
我有一个Class类型的数组,它存储上面的类。
Class[] classes = {A.class,B.class,C.class};
然后我有一个包含A,B和C类型对象的List
List<?> list = new ArrayList<?>();
list.add(new A());
list.add(new B());
list.add(new C());
我想通过循环访问列表中的对象。我还想访问对象A,B和C的方法。所以,我尝试使用classes数组来构建它们。
for(int i = 0; i < classes.length;i++)
{
classes[i].cast(list.get(i)).getName(); //A,B,and C have method getName() in common
}
这给了我一个编译器错误:“没有为类型Object
定义方法getName()编译器有没有办法忽略这种类型的转换直到运行时(我相信它会在运行时工作)?或者是否有其他解决方案可以达到我的预期(我正在寻找一种不涉及更改A,B和C类代码的解决方案)
答案 0 :(得分:4)
您应该使用方法getName()
实现一个接口,我们称之为Nameable
:
public interface Nameable {
public String getName();
}
然后让这三个类A
B
和C
实现Nameable
。这将允许您将列表声明为List<Nameable>
,并且在访问其元素时不需要进行转换来调用此方法。
编辑:好的,既然您无法修改这些类,那么可以执行与@JB Nizet建议类似的操作。但是,不要在循环中动态查找方法,因为这很昂贵。为每个班级静态查找一次这些方法,并将其保存在密钥为Map
的{{1}}中:
Class
然后从循环中查找方法:
private static final Map<Class<?>, Method> getNameMap = new HashMap<Class<?>, Method>();
static {
try {
getNameMap.put(A.class, A.class.getMethod("getName"));
getNameMap.put(B.class, B.class.getMethod("getName"));
getNameMap.put(C.class, B.class.getMethod("getName"));
}
catch (NoSuchMethodException nsme) {
//handle method not being found
}
}
答案 1 :(得分:2)
您不需要强制转换:A
实例是A
实例,而转换不会更改其类型。由于您在编译时不知道元素的类型是什么,因此转换对您没有帮助。
您必须做的是通过反射调用getName
方法:
Class<?> clazz = list.get(i).getClass();
Method m = clazz.getMethod("getName");
m.invoke(list.get(i));
答案 2 :(得分:2)
答案是使用 instanceof 运算符。如果列表中的类已知,这似乎是合理的。这是一些代码:
public static void main(String[] args) { A aObject; B bObject; C cObject; List list = new ArrayList(); list.add(new A()); list.add(new B()); list.add(new C()); list.add(new String()); for (Object current : list) { if (current instanceof A) { aObject = (A)current; System.out.println(aObject.getName()); } else if (current instanceof B) { bObject = (B)current; System.out.println(bObject.getName()); } else if (current instanceof C) { cObject = (C)current; System.out.println(cObject.getName()); } else { System.out.print("Unexpected class: "); System.out.println(current.getClass()); } } }
这是使用Reflection执行此操作的(稍微)更详细(然后在其他答案中)的示例。如果列表中的类未知或者有很多类,这似乎是合理的。下面注释掉的行使用Apache Commons Lang类MethodUtils。
public static void main(String[] args) { List list = new ArrayList(); Method getNameMethod; list.add(new A()); list.add(new B()); list.add(new C()); list.add(new String()); for (Object current : list) { // getNameMethod = MethodUtils.getAccessibleMethod(current.getClass(), "getName", (Class[])null); try { getNameMethod = current.getClass().getMethod("getName"); } catch (SecurityException exception1) { getNameMethod = null; } catch (NoSuchMethodException exception1) { getNameMethod = null; } if (getNameMethod != null) { try { System.out.println(getNameMethod.invoke(current, (Object[])null)); } catch (IllegalArgumentException exception) { // TODO Implement this catch block. } catch (IllegalAccessException exception) { // TODO Implement this catch block. } catch (InvocationTargetException exception) { // TODO Implement this catch block. } } else { System.out.print("Unexpected class: "); System.out.println(current.getClass()); } } }
答案 3 :(得分:0)
因为编译器无法检查该列表中的对象是否具有相应的getName()
方法。如果该列表中有D类对象怎么办?解决方案是使用方法getName()
import java.util.ArrayList;
import java.util.List;
public class InterfaceExample {
interface I {
String getName();
}
static class A implements I{
...
}
static class B implements I {
...
}
static class C implements I {
...
}
public static void main(String[] args) {
List<I> list = new ArrayList<I>();
list.add(new A());
list.add(new B());
list.add(new C());
for(I i : list) {
i.getName();
}
}
}
答案 4 :(得分:0)
你应该为此目的使用接口来制作
interface Nameable { public String getName() }
class A implements Nameable { ... }
class B implements Nameable { ... }
...
这明显违背了你的限制(不改变A,B,C代码),但这是正确的方法。
如果你真的想保持A,B,C源不受影响,你可以使用中间对象:
interface Nameable { public String getName() }
class AC extends A implements Nameable { ... }
class BC extends B implements Nameable { ... }
class CC extends C implements Nameable { ... }
或者最后你也可以使用合成
class AC implements Nameable
{
A instance;
public String getName()
{
return instance.getName();
}
}
但我个人坚持第一个解决方案,这是正确的OO方式。
答案 5 :(得分:0)
如果您不想(或不能)更改类A
,B
和C
的定义,那么您最有可能的选项是:通过反射调用方法。
目前,Java的静态类型不能保证list
中的元素具有 getName()
方法 - 因为它可以保证它们是{的实例{1}}。
即使你的演员也无济于事,因为你的java.lang.Object
数组遇到了完全相同的问题。请注意,classes
实际上是通用的,其类型参数对应于要转换的实际类。由于数组的元素只有原始类型Class<E>
(大致相当于Class
),因此Class<? extends Object>
调用的结果再次是classes[i].cast()
的实例。因此,您无法在其上拨打java.lang.Object
。
除非这三个类有一些共同的超接口定义getName
,否则你将无法满足编译器它是一种以这种方式调用它们的有效方法。
解决这个问题的一个非常基本的方法就是以不同的方式处理每个类 - 使用getName()
检查然后显式转换为instanceof
,A
或B
。这将使编译器感到满意,并且有一些静态类型检查(例如,如果在C
中重命名该方法,您的代码将停止编译),但是会使您必须编写的代码量增加三倍,并冒着你以后跳过新课程A
的风险。
另一个选择是反射 - 在运行时直接从对象获取D
方法并调用它:
getName
这“正常”,但它的性能很差,并且意味着在运行时获得for(int i = 0; i < list.length;i++)
{
final Object obj = list.get(i);
final Method m = obj.getClass.getMethod("getName"); //A,B,and C have method getName() in common
m.invoke(obj);
// Exception-handling code omitted
}
之前,不会对任何类的更改进行选取。
最好的解决方案是引入界面并更改类。
答案 6 :(得分:0)
使用反射,可以创建一个实用程序方法来调用适当定义它的任何对象的getName()方法(我没有猜到任何参数,并返回String
):
static String executeGetName(Object o) throws SecurityException,
NoSuchMethodException, IllegalArgumentException,
IllegalAccessException, InvocationTargetException {
Method method = o.getClass().getMethod("getName");
return (String) method.invoke(o);
}