[英]ViewModel has no zero argument constructor at java.lang.Class.newInstance(Native Method)
[英]Java: newInstance of class that has no default constructor
我正在嘗試為學生的作業構建一個自動測試框架(基於jUnit,但這並不重要)。 他們必須為某些類創建構造函數,並為它們添加一些方法。 后來,通過我提供的測試功能,他們將檢查它們是否正常。
我想做的是, 通過反射 ,創建一個我想要測試的類的新實例。 問題是,有時候, 沒有默認的構造函數 。 我不關心這個, 我想創建一個實例並自己初始化實例變量 。 有沒有辦法做到這一點? 如果以前曾經問過這個問題我很抱歉,但我找不到任何答案。
提前致謝。
調用Class.getConstructor()
,然后調用Constructor.newInstance()
傳入適當的參數。 示例代碼:
import java.lang.reflect.*;
public class Test {
public Test(int x) {
System.out.println("Constuctor called! x = " + x);
}
// Don't just declare "throws Exception" in real code!
public static void main(String[] args) throws Exception {
Class<Test> clazz = Test.class;
Constructor<Test> ctor = clazz.getConstructor(int.class);
Test instance = ctor.newInstance(5);
}
}
這是一個通用的解決方案,不需要javassist或其他字節碼“操縱器”。 雖然,它假設構造函數除了簡單地將參數分配給相應的字段之外沒有做任何其他操作,因此它只選擇第一個構造函數並創建一個具有默認值的實例(即0表示int,null表示Object等)。
private <T> T instantiate(Class<T> cls, Map<String, ? extends Object> args) throws Exception
{
// Create instance of the given class
final Constructor<T> constr = (Constructor<T>) cls.getConstructors()[0];
final List<Object> params = new ArrayList<Object>();
for (Class<?> pType : constr.getParameterTypes())
{
params.add((pType.isPrimitive()) ? ClassUtils.primitiveToWrapper(pType).newInstance() : null);
}
final T instance = constr.newInstance(params.toArray());
// Set separate fields
for (Map.Entry<String, ? extends Object> arg : args.entrySet()) {
Field f = cls.getDeclaredField(arg.getKey());
f.setAccessible(true);
f.set(instance, arg.getValue());
}
return instance;
}
PS適用於Java 1.5+。 該解決方案還假設沒有SecurityManager管理器可以阻止調用f.setAccessible(true)
。
如果您還沒有使用過模擬框架(比如ezmock),我強烈建議您嘗試一下。
我可能是錯的,這可能對你沒什么幫助,但是從你的帖子中我可以收集到的東西似乎可能是你正在尋找的嘲諷(即使我認識到它與你所要求的無關對於。
編輯:回應評論。
不,現代模擬框架允許您從“無”創建任何類的“假”實例並將其傳遞,就好像它是類的實例一樣。 它不需要接口,它可以是任何類。 此外,方法可以編寫腳本以返回一個值序列,從簡單的總是返回“7”到“當用arg = 7調用時返回5第一個調用,6返回第一個調用,7調用第三個調用”。
它通常與測試框架結合使用,以提供一個引用類來傳遞給您正在測試的類。
這可能不是您正在尋找的,但您提到了單元測試並手動初始化變量,因此看起來這可能最終會派上用場。
我使用以下代碼創建了傳入的任何類型名稱的泛型對象列表。它使用類中的所有set方法來設置通過結果集傳入的所有值。 我發布這個以防萬一也有人對此感興趣。
protected List<Object> FillObject(ResultSet rs, String className)
{
List<Object> dList = new ArrayList<Object>();
try
{
ClassLoader classLoader = GenericModel.class.getClassLoader();
while (rs.next())
{
Class reflectionClass = classLoader.loadClass("models." + className);
Object objectClass = reflectionClass.newInstance();
Method[] methods = reflectionClass.getMethods();
for(Method method: methods)
{
if (method.getName().indexOf("set") > -1)
{
Class[] parameterTypes = method.getParameterTypes();
for(Class pT: parameterTypes)
{
Method setMethod = reflectionClass.getMethod(method.getName(), pT);
switch(pT.getName())
{
case "int":
int intValue = rs.getInt(method.getName().replace("set", ""));
setMethod.invoke(objectClass, intValue);
break;
case "java.util.Date":
Date dateValue = rs.getDate(method.getName().replace("set", ""));
setMethod.invoke(objectClass, dateValue);
break;
case "boolean":
boolean boolValue = rs.getBoolean(method.getName().replace("set", ""));
setMethod.invoke(objectClass, boolValue);
break;
default:
String stringValue = rs.getString(method.getName().replace("set", ""));
setMethod.invoke(objectClass, stringValue);
break;
}
}
}
}
dList.add(objectClass);
}
}
catch (Exception e)
{
this.setConnectionMessage("ERROR: reflection class loading: " + e.getMessage());
}
return dList;
}
您可以使用Class.getConstructor或Class.getConstructors ,然后使用方法Constructor.newInstance初始化您要使用的對象。
您可以在作業中分發以下源代碼。 告訴學生將其包含在源代碼中。 除非他們使用適當的簽名編寫Assignment類,否則他們的代碼將無法編譯。 編譯器會為您進行簽名檢查。
然后你的測試程序不需要使用反射。 只需實例化一個AssignmentFactory並使用正確的參數調用make方法。
如果您使用這個想法,您的新挑戰將是一些學生修改AssignmentFactory以適合他們的Assignment類(打破您的測試程序)。
package assignment ;
public class AssignmentFactory
{
public AssignmentFactory ( )
{
super ( ) ;
}
public AssignmentFactory make ( .... parameters )
{
return new Assignment ( .... arguments ) ;
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.