动态转换java编译器错误

时间:2011-08-10 14:34:36

标签: java casting

我有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类代码的解决方案)

7 个答案:

答案 0 :(得分:4)

您应该使用方法getName()实现一个接口,我们称之为Nameable

public interface Nameable {

    public String getName();
}

然后让这三个类A BC实现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)

如果您不想(或不能)更改类ABC的定义,那么您最有可能的选项是:通过反射调用方法。

目前,Java的静态类型不能保证list 中的元素具有 getName()方法 - 因为它可以保证它们是{的实例{1}}。

即使你的演员也无济于事,因为你的java.lang.Object数组遇到了完全相同的问题。请注意,classes实际上是通用的,其类型参数对应于要转换的实际类。由于数组的元素只有原始类型Class<E>(大致相当于Class),因此Class<? extends Object>调用的结果再次是classes[i].cast()的实例。因此,您无法在其上拨打java.lang.Object


除非这三个类有一些共同的超接口定义getName,否则你将无法满足编译器它是一种以这种方式调用它们的有效方法。

解决这个问题的一个非常基本的方法就是以不同的方式处理每个类 - 使用getName()检查然后显式转换为instanceofAB。这将使编译器感到满意,并且有一些静态类型检查(例如,如果在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);
}