2009-03-30 6 views
4

나는 다음 클래스 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) 

는 점에 유의 걸릴 방법 매개 변수로 개체와 메서드는 없습니다.

이제 컴파일 된 시간에 값 클래스 유형을 결정할 방법이 없습니다. 나는 런타임 동안 나의 가치 클래스 유형만을 결정할 수있다. 따라서 컴파일 된 시간 동안 메서드 시그니처를 모르는 경우 어떻게 호출 할 올바른 메서드를 결정할 수 있습니까?

내 코드는 다음과 같습니다 :

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와 아마 추한 내 문제를 해결하는 것입니다. 그러나, 만약 내가 추한 것을 원하지 않는다면 ... instanceof으로, 그렇게 할 수있는 더 좋은 방법이 있습니까?

답변

3

한 가지 방법의 목록을로드하는 것입니다 ... 당신은 여전히 ​​자동 권투 getMethod() 작동하지 않기 때문에 기본 형식에 대한 몇 가지 사용자 정의를 할 생각 메소드를 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있는 방법을 찾아보고 해당 인스턴스를 사용하고자 할 때.

이번에는 단순화되었지만 이번에는 전체 코드로 이것을 보여줍니다. 완전히 일반화되기 위해 코드는 아래와 같이 조금 더 복잡해집니다. 사용법에 따라 달라지는 프리미티브에 대해 걱정할 필요가 없거나 인터페이스 나 수퍼 클래스에 대해 걱정할 필요가없는 경우 아래 예제를 단순화 할 수 있습니다.

걱정할 필요가있는 인수 나 인터페이스 또는 수퍼 클래스에 중복이 없도록 보장 할 수 있다면 복잡한 논리를 모두 초기화로 옮길 수 있습니다 (1 ms 이상). 이 경우 findMethodToInvoke()의 모든 로직이 생성자로 이동합니다. 여기서 생성자는 찾은 각 메소드의 모든 인터페이스와 수퍼 클래스를 반복하여 parameterTypeMap에 추가합니다.그렇지 않으면

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

꽤 악마 적입니다. 완전성을 위해, 당신은 실제로 적절한 방법을 찾고 그 매개 변수로 호출하는 스 니펫을 게시 할 수 있습니까? 또한 'map'을 typeToOverload와 같은 것으로 이름을 바꿔 조금 더 명확하게 만들 수 있습니까? –

+0

언뜻보기에이 솔루션은 TofuBeer 's에 비해 복잡하다고 생각했습니다. 몇 가지 상세한 연구를 마친 후, 나는 그것이 2 가지 추가 사례를 다루고 있음을 깨달았다. (1) 원시적 방법 매개 변수 (2) 수퍼 클래스와 인터페이스. 따라서 나는 대답으로 그것을 받아들이기로 결정했다. –

+0

주목해야 할 한 가지 더 : if (clazzes [0] .isPrimitive()) { handlePrimitive (method, clazzes [0]); } 로 작성해야합니까? if (clazzes [0] .isPrimitive()) { handlePrimitive (method, clazzes [0]); 계속; } –

0

나는 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."); 
    } 
} 

다른 방법으로 당신이 반사를 사용할 수 있습니다 : 당신은 보조 방법에 instanceof 표현하는 것이 코드 추악한 추출물하게 생각합니다. 심지어 반사 와 나는이 처리의

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) { 
    } 

} 
0

:

return parameterTypeMap.get(test.getClass()); 

하지만이 최적화없이 전체 보편성과

가 여기에이 작업을 수행하는 방법에 내 예 : 당신이 최적화를 할 경우, findMethodToInvoke() 한 줄이된다 서브 클래스가이 반사를 사용할 수있다 (당신이 경우에 당신은 아직도 그것을 할 수 있지만 어렵게 될 것입니다, 당신이 경우에 알려주세요) :

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); 
    } 
} 

단점은 컴파일러가 없다는 것입니다 존재하지 않는 메소드를 호출하려고하면 알려줍니다.

위의 코드에서 예외 처리는 완벽하지 않지만 반사 부분에 초점을 맞추고 싶습니다.

인스턴스를 사용하는 것이 컴파일 시간 유형 안전성이 있다는 관점에서 볼 때 더 좋습니다. 새로운 방법이 추가되면 반사를 업데이트 할 필요가 없습니다.