2010-07-08 39 views
2

我在寫一個模型中有一個簽名爲public void setFoo(int newFoo)的方法。我使用下面的代碼從我的控制器中調用它:在Java中取消裝箱和反射?

protected void setModelProperty(String propertyName, Object newValue) { 

    for (AbstractModel model: registeredModels) { 
     try { 
      Method method = model.getClass(). 
       getMethod("set"+propertyName, new Class[] { 
                newValue.getClass() 
               } 
         ); 
      method.invoke(model, newValue); 

     } catch (Exception ex) { 
      // Handle exception. 
      System.err.println(ex.toString()); 
     } 
    } 
} 

調用拋出像controller.setModelProperty("Foo",5);導致異常此方法:java.lang.NoSuchMethodException: foo.bar.models.FooModel.setFoo(java.lang.Integer) - 它看起來像int被裝箱爲Integer,這與setFoo的簽名不匹配。

有沒有什麼辦法說服這個反射代碼通過5(或任何int我傳入)作爲一個整數,沒有拳擊?或者我必須明確地在我的模型和unbox中創建public void setFoo(Integer newFoo),然後調用原始的setFoo?

+0

這是奇怪的。 Method.invoke()的Javadocs表示單個參數自動解包以匹配原始形式參數,所以看起來這應該不成問題。我在這臺機器上沒有編譯器,或者我會做一些測試。 – 2010-07-08 15:21:24

+1

@mmyers但newValue是一個盒裝Integer,因此getMethod將在調用Method.invoke(...)之前嘗試並返回帶有簽名'setFoo(Integer)'的(非遠程)方法。 – MHarris 2010-07-08 15:24:06

+0

@MHarris:那樣做。我看到了getMethod調用,但由於格式不正常,我認爲這是一種自我實現的方法,而不是標準的Class方法。 – 2010-07-08 15:41:47

回答

4

你可以專注你的setModelProperty你希望的任何原語與使用:

protected void setModelProperty(String propertyName, int newValue) { /* ... */ } 

或者,你可以使用instanceof上NEWVALUE檢查盒裝原語:

Class[] classes; 
if (newValue instanceof Integer) { 
    classes = new Class[] { int.class }; 
} else if (newValue instanceof Double) { 
    /* etc...*/ 
} else { 
    classes = new Class[] {newValue.getClass() }; 
} 

或者最後,如果你有setFoo的源代碼,你可以將它改爲採用盒裝Integer而不是int - 開銷通常可以忽略不計。

-1

你嘗試

Integer.valueOf(5)通過5一起作爲一個整數,而不是一個int?

1

當setModelProperty()被調用時,newValue被裝箱爲整數。這是唯一可以調用的方式; 'int'不是Object的一個實例。 newValue.getClass()返回「Integer」,因此調用getMethod()失敗。

如果你想在這裏使用原語,你需要一個特殊版本的setModelProperty。或者你可以寫一個方法setFoo(Integer)。

或者更一般你可以寫:

if (newValue.getClass()==Integer.class) { 
    // code to look for a method with an int argument 
} 
2

有什麼辦法說服這個 反射代碼可通過5(或 任何詮釋我通過)作爲一個整數, 沒有拳擊?

不是在您的方法簽名說Object newValue時,因爲int永遠不可能是Object。保持方法通用的一種方法是將有來電傳中,類型明確,即:

protected void setModelProperty(String propertyName, Object newValue, Class type) { 

或者,你可以測試的newValue類型,看它是否是一個原始的包裝,並在這種情況下看兩個該方法的原始和包裝版本。但是,當用戶通過null時,這不起作用。其實,這種方法在這種情況下根本不起作用...

+0

通過這種方式,您可以重載方法來接收2個參數,如果它是普通對象,並且調用'setModelProperty(propertyName,newValue,newValue.getClass())''。但是如果你知道這個值必須是一個基本類型,你可以創建一個調用'setModelProperty(propertyName,Object newValue,Integer.TYPE)'的參數'String,int'的方法。不過,您將需要一個用於您想使用的每個基元。 – 2010-07-08 15:43:30

0

問題不在於調用,而是在獲取方法,因爲您使用的Integer.class與int.class不同。

您可以通過執行第二個電話,如果第一個未能獲得方法解決它:

protected void setModelProperty(String propertyName, Object newValue) { 

    for (AbstractModel model: registeredModels) { 
     Method method = null; 
     try { 
      method = model.getClass(). 
       getMethod("set"+propertyName, new Class[] { 
                newValue.getClass() 
               } 
         ); 
     } catch (Exception ex) { 
      try { 
       if (canBoxPrimitive(newValue.getClass()) { 
        method = model.getClass(). 
          getMethod("set"+propertyName, new Class[] { 
                primitiveClass(newValue.getClass()) 
               } 
       } 
      } 
      catch (Exception ex) { 
       // handle it 
      } 
     } 

     method.invoke(model, newValue); 
    } 
} 

對於選項編寫函數:布爾canBoxPrimitive(類)類primitiveClass(類)你可以看看這裏:Dynamically find the class that represents a primitive Java type

0

Outoboxing將完成,但你正在尋找錯誤的方法。參數類型數組不正確。對於int類型將是Integer.TYPE而不是Integer.class。

嘗試一些像下面得到它,無論工作parameer型(outoboxing將做到這一點):

protected void setModelProperty(String propertyName, Object newValue) { 

    for (AbstractModel model: registeredModels) { 
     try { 
      Method method = findSetter(model.getClass(), propertyName); 
      method.invoke(model, newValue); 

     } catch (Exception ex) { 
      // Handle exception. 
      System.err.println(ex.toString()); 
     } 
    } 
} 

private Method findSetter(Class<?> type, String propertyName) { 
    for (Method methos : type.getMethods()) { 
     if (method.getName().equalsIgnoreCase("set" + propertyName) && method.getParameterTypes().length == 1) { 
      return method; 
     } 
    } 
    throw new NoSuchMethodError("No setter for " + propertyName); 
}