Java泛型场景下,如何通过类字面量实现向下转型调用重载方法?
问题分析与解决方案
你的方案之所以不生效,核心原因是:在遍历List<MyObj<?>>时,myObj.getClassLiteral().cast(myObj.body())返回的是通配符类型?,编译器无法确定它具体是A还是B。而方法重载是编译时确定的,必须有明确的类型信息才能匹配对应的todo(A)或todo(B),因此编译器没法自动选择正确的重载方法。
下面是几种可行的解决思路:
方案1:让MyObj自身负责调用对应方法
修改MyObj为抽象类,添加抽象方法让子类实现具体的调用逻辑,利用子类的泛型类型绑定来明确参数类型:
abstract class MyObj<T> { Class<T> classLiteral; T body; // 抽象方法,由子类实现具体调用 abstract void invokeTodo(); } // 针对A类型的实现 class MyObjA extends MyObj<A> { @Override void invokeTodo() { todo(body); // 编译器明确知道body是A,直接匹配todo(A) } } // 针对B类型的实现 class MyObjB extends MyObj<B> { @Override void invokeTodo() { todo(body); // 同理匹配todo(B) } }
遍历的时候直接调用:
for(MyObj<?> myObj : myObjList) { myObj.invokeTodo(); }
方案2:用访问者模式做类型分发
如果不想为每个类型创建子类,可以用访问者模式统一处理类型判断和方法调用:
首先定义访问者接口:
interface MyObjVisitor { void handleA(A a); void handleB(B b); }
实现访问者,绑定todo方法:
MyObjVisitor todoVisitor = new MyObjVisitor() { @Override public void handleA(A a) { todo(a); } @Override public void handleB(B b) { todo(b); } };
给MyObj添加accept方法,根据类型分发到访问者的对应方法:
class MyObj<T> { Class<T> classLiteral; T body; void accept(MyObjVisitor visitor) { if (classLiteral == A.class) { visitor.handleA((A) body); } else if (classLiteral == B.class) { visitor.handleB((B) body); } // 后续新增类型时,在这里加分支即可 } }
遍历调用:
for(MyObj<?> myObj : myObjList) { myObj.accept(todoVisitor); }
方案3:反射调用重载方法(不推荐,仅作备选)
如果前面的方案都不适用,可以用反射根据实际参数类型找到对应的todo方法并调用:
for(MyObj<?> myObj : myObjList) { Object targetBody = myObj.body(); try { // 找到参数类型匹配的todo方法 Method todoMethod = YourCurrentClass.class.getMethod("todo", targetBody.getClass()); // 调用方法,第一个参数是todo方法所属的实例(静态方法传null) todoMethod.invoke(this, targetBody); } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { // 处理类型不匹配或调用失败的情况 e.printStackTrace(); } }
注意:反射会带来性能损耗,而且编译时无法检查方法的正确性,只有在万不得已时才建议使用。
内容的提问来源于stack exchange,提问作者BITniki
相关产品推荐
相关产品推荐

