我正在使用以下类org.apache.poi.hssf.usermodel.HSSFCell
,其中列出了以下方法:
void setCellValue(boolean value)
void setCellValue(java.util.Calendar value)
void setCellValue(java.util.Date value)
void setCellValue(double value)
void setCellValue(HSSFRichTextString value)
void setCellValue(java.util.Calendar value)
void setCellValue(HSSFRichTextString value)
请注意,没有使用Object作为方法参数的方法。
现在,我无法在编译时确定我的值类类型。我只能在运行时确定我的值类类型。因此,如果在编译时不知道方法签名,我如何确定要调用的正确方法?
我的代码如下:
final int rowCount = tableModel.getRowCount();
for (int i = 0; i < rowCount; i++) {
final HSSFRow row = sheet.createRow(i + 1);
for (int j = 0; j < columnCount; j++) {
final Object object = tableModel.getValueAt(i, j);
final Class myClass = tableModel.getColumnClass(j);
// How to perform casting during compiled time, and invoke
// the setCellValue with correct signature?
if (object != null) {
row.createCell(j).setCellValue(??); // Does not accept Object!
}
}
}
如果...... instanceof
的其他内容可以解决我的问题,也许很难看。但是,如果我不想要丑陋的if ...使用instanceof
,有没有更好的方法呢?
答案 0 :(得分:3)
处理此问题的一种方法是在运行时将该方法列表加载到Map
,然后对于每个调用,使用Map
。也就是说,这样的事情(这个代码被简化并省略了错误检查):
Map<? extends Object, Method> map;
Method[] methods = Setters.class.getMethods();
for (Method method : methods) {
if (method.getName().equals("setCellValue")) {
map.put(method.getParameterTypes()[0], method);
}
}
然后当你想调用它时,在map
参数类型中查找Method并使用该实例。
要显示此功能,请再次使用简化但这次完整代码。请注意,为了完全通用,代码会变得更复杂,如下所示。如果您不必担心原语(这取决于您的用法)或者您不必担心接口或超类,那么您可以简化下面的示例。
此外,如果您可以保证在您不必担心的参数中的接口或超类中没有重叠,您可以将所有复杂的逻辑移动到初始化(如果需要1 ms则无关紧要更长)。在这种情况下,findMethodToInvoke()
中的所有逻辑都将被移动到构造函数中,您可以在其中遍历您找到的每个方法的所有接口和超类,并将它们添加到parameterTypeMap中。如果您执行此优化,则findMethodToInvoke()
将成为一行:
return parameterTypeMap.get(test.getClass());
但是如果没有这种优化并具有完全的通用性,这是我的如何做到这一点的例子:
import java.lang.reflect.*;
import java.util.*;
public class Test {
private final Map<Object, Method> parameterTypeMap = new HashMap<Object, Method>();
private final Object[] tests = {Double.valueOf(3.1415),
Boolean.TRUE,
new Date(),
new GregorianCalendar(),
new HashMap<Object, Object>()};
public Test() {
Method[] methods = Setters.class.getMethods();
for (Method method : methods) {
if (method.getName().equals("setCellValue")) {
Class<?>[] clazzes = method.getParameterTypes();
if (clazzes.length != 1) {
continue;
}
if (clazzes[0].isPrimitive()) {
handlePrimitive(method, clazzes[0]);
}
parameterTypeMap.put(clazzes[0], method);
}
}
}
// See http://java.sun.com/javase/6/docs/api/java/lang/Class.html#isPrimitive()
private void handlePrimitive(Method method, Class<?> clazz) {
if (clazz == Boolean.TYPE) {
parameterTypeMap.put(Boolean.class, method);
} else if (clazz == Double.TYPE) {
parameterTypeMap.put(Double.class, method);
} // ... and so on for the other six primitive types (void doesn't matter)
}
public void doTests(Setters setter) {
for (Object test : tests) {
Method method = findMethodToInvoke(test);
if (method == null) {
System.out.println("Nothing found for " + test.getClass());
continue;
}
try {
method.invoke(setter, test);
} catch (Exception e) {
e.printStackTrace();
}
}
}
private Method findMethodToInvoke(Object test) {
Method method = parameterTypeMap.get(test.getClass());
if (method != null) {
return method;
}
// Look for superclasses
Class<?> x = test.getClass().getSuperclass();
while (x != null && x != Object.class) {
method = parameterTypeMap.get(x);
if (method != null) {
return method;
}
x = x.getSuperclass();
}
// Look for interfaces
for (Class<?> i : test.getClass().getInterfaces()) {
method = parameterTypeMap.get(i);
if (method != null) {
return method;
}
}
return null;
}
public static void main(String[] args) {
Test test = new Test();
test.doTests(new Setters());
}
}
class Setters {
public void setCellValue(boolean value) {
System.out.println("boolean " + value);
}
public void setCellValue(double value) {
System.out.println("double " + value);
}
public void setCellValue(Calendar value) {
System.out.println("Calendar " + value);
}
public void setCellValue(Date value) {
System.out.println("Date " + value);
}
public void setCellValue(Map<?, ?> value) {
System.out.println("Map " + value);
}
}
答案 1 :(得分:0)
我认为instanceof
是要走的路。如果您认为它使您的代码难以将instanceof
表达式提取到辅助方法中:
public void setCellValue(HSSFCell cell, Object value) {
if (null == cell)
throw new IllegalArgumentException("cell");
if (null == value)
throw new IllegalArgumentException("value");
if (value instanceof Double)
cell.setCellValue((Double)value); // auto-boxing will handle this
else if (value instanceof Boolean) {
cell.setCellValue((Boolean)value); // auto-boxing will handle this
} else if (value instanceof Calendar) {
cell.setCellValue((Calendar)value);
} else if ...
.....
} else {
throw new UnsupportedTypeException("Object of class " + Value.class.getName() + " not supported.");
}
}
或者你可以使用反射。即使使用反射我认为你仍然需要对基本类型进行一些自定义,因为自动装箱对getMethod()
不起作用...
public void invokeSetCellValue(HSSFCell cell, Object obj) {
try {
Class<?> clazz = obj.getClass();
if (obj instanceof Double) {
clazz = double.class;
} else if (obj instanceof Boolean) {
clazz = boolean.class;
}
Method m = HSSFCell.class.getMethod("setCellValue", clazz);
m.invoke(cell, obj);
} catch (SecurityException e) {
} catch (NoSuchMethodException e) {
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
} catch (InvocationTargetException e) {
}
}
答案 2 :(得分:0)
如果你没有子类(如果你这样做,你仍然可以这样做,但如果你这样做会更难,请告诉我)你可以使用反射:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Main
{
public static void main(final String[] argv)
throws NoSuchMethodException,
IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
{
final Object o;
if(argv.length == 0)
{
o = "Hello";
}
else
{
o = Integer.valueOf(42);
}
callFoo(o);
}
private static void callFoo(final Object o)
throws NoSuchMethodException,
IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
{
Method method;
method = Main.class.getDeclaredMethod("foo", o.getClass());
method.invoke(null, o);
}
private static void foo(final String val)
{
System.out.println("foo(String) -> " + val);
}
private static void foo(final Integer val)
{
System.out.println("foo(Integer) -> " + val);
}
}
缺点是如果您尝试调用不存在的方法,则没有编译器告诉您。
上面代码中的异常处理是完全废话,但我想专注于反射部分。
从编译时类型安全的角度来看,使用实例更好。如果添加新方法,则不必更新反射。