我已经在Google和StackOverflow上搜索了但我还没有找到答案..
我尝试使用构造函数按名称实例化一个类,而不知道构造函数的参数类型。
它必须与此类似:
// ... Load class using ClassLoader if not in classpath ...
// These are given as arguments to the function:
String pncn = "Some.Package.UnknownClass"; // packagename classname
Object[] oa =new Object[] {
"Test0",
new Random()
};///object array
Class cl = Class.forName(pncn);
Class[] ca = /* ? */;
// (Here I need to get all constructor argument classes by the objects in 'oa'
// Get the constructor with the given arg types of 'ca'
Constructor co = cl.getConstructor(ca);
//And then instantiate the class
Object ro = co.newInstance(ca) ;
/*//return it:
return ro;*/;
所以基本上问题是如何将不同类型的对象数组(在示例中为' oa')转换为Class数组?
我计划使用此方法通过javascript按名称和参数创建类的实例。
答案 0 :(得分:1)
对于包含大量假设的简单情况:您循环遍历oa
,在每个对象上调用getClass()
并使用结果填充ca
。
如果您的例程必须更加健壮,则必须考虑更多情况:
null
。由于null
对于每种引用类型都是可接受的,因此您可能必须迭代所有可能的构造函数以便找到匹配项。不能保证是一个明确的匹配。getClass()
将返回java.lang.Integer
而不是Integer.TYPE
的类对象,基本类型int
的类对象。简而言之:您必须重新实现编译器已经为静态情况做的事情。
答案 1 :(得分:0)
试试这个:
private static Class<?>[] getClasses(Object[] oa) {
if (oa == null) return new Class[0];
Class<?>[] ret = new Class[oa.length];
for (int i = 0; i < oa.length; i++) {
ret[i] = oa[i].getClass();
}
return ret;
}
我没有广泛测试过,但它似乎找到了原始类型的args的匹配,并选择了最具体的ctor来创建新对象。但是,它可能有错误(我发现它没有自动转换所选ctor的原始值,例如,如果选择ctor有short
param而你在对象数组中传递int
,它会在newInstance
内失败。随意改进:)
class B {
@Override
public String toString() {
return "B{}";
}
}
class D extends B {
@Override
public String toString() {
return "D{}";
}
}
class E extends D {
@Override
public String toString() {
return "E{}";
}
}
class Test {
final short primitive;
final B obj;
Test() {
System.out.println("()");
primitive = 42;
obj = new D();
}
Test(short primitive, B obj) {
System.out.println("B()");
this.primitive = primitive;
this.obj = obj;
}
Test(short primitive, D obj) {
System.out.println("D()");
this.primitive = primitive;
this.obj = obj;
}
@Override
public String toString() {
return "Test{" +
"primitive=" + primitive +
", obj=" + obj +
'}';
}
}
class Junk {
// sorts lists of param classes in order from the most to the least specific one
private static final Comparator<? super Constructor<?>> CTOR_COMPARATOR = new Comparator<Constructor<?>>() {
@Override
public int compare(Constructor<?> ctorA, Constructor<?> ctorB) {
Class<?>[] params1 = ctorA.getParameterTypes();
Class<?>[] params2 = ctorB.getParameterTypes();
if (params1.length != params2.length)
throw new IllegalArgumentException(ctorA + " can't be compared to " + ctorB);
for (int i = 0; i < params1.length; i++) {
Class<?> aClass = params1[i];
Class<?> bClass = params2[i];
if (!aClass.equals(bClass)) {
if (aClass.isAssignableFrom(bClass)) return 1;
if (bClass.isAssignableFrom(aClass)) return -1;
throw new IllegalArgumentException(ctorA + " can't be compared to " + ctorB +
": args at pos " + i + " aren't comparable: " + aClass + " vs " + bClass);
}
}
return 0;
}
};
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
System.out.println(tryToCreateBestMatch(Test.class, new Object[]{(short)1, new B()}));
System.out.println(tryToCreateBestMatch(Test.class, new Object[]{(short)1, new D()}));
System.out.println(tryToCreateBestMatch(Test.class, new Object[]{null, new B()}));
System.out.println(tryToCreateBestMatch(Test.class, new Object[]{(short)1, new E()}));
System.out.println(tryToCreateBestMatch(Test.class, new Object[]{}));
System.out.println(tryToCreateBestMatch(Test.class, new Object[]{"will fail"}));
}
private static <T> T tryToCreateBestMatch(Class<T> aClass, Object[] oa) throws InstantiationException, IllegalAccessException, InvocationTargetException {
//noinspection unchecked
Constructor<T>[] declaredConstructors = (Constructor<T>[]) aClass.getDeclaredConstructors();
Class<?>[] argClasses = getClasses(oa);
List<Constructor<T>> matchedCtors = new ArrayList<>();
for (Constructor<T> ctr : declaredConstructors) {
Class<?>[] parameterTypes = ctr.getParameterTypes();
if (ctorMatches(parameterTypes, argClasses)) {
matchedCtors.add(ctr);
}
}
if (matchedCtors.isEmpty()) return null;
Collections.sort(matchedCtors, CTOR_COMPARATOR);
return matchedCtors.get(0).newInstance(oa);
}
private static boolean ctorMatches(Class<?>[] ctrParamTypes, Class<?>[] argClasses) {
if (ctrParamTypes.length != argClasses.length) return false;
for (int i = 0; i < ctrParamTypes.length; i++) {
Class<?> ctrParamType = ctrParamTypes[i];
Class<?> argClass = argClasses[i];
if (!compatible(ctrParamType, argClass)) return false;
}
return true;
}
private static boolean compatible(Class<?> ctrParamType, Class<?> argClass) {
if (ctrParamType.isAssignableFrom(argClass)) return true;
if (ctrParamType.isPrimitive()) return compareAgainstPrimitive(ctrParamType.getName(), argClass);
return false;
}
private static boolean compareAgainstPrimitive(String primitiveType, Class<?> argClass) {
switch (primitiveType) {
case "short":case "byte" :case "int":case "long":
return INTEGER_WRAPPERS.contains(argClass.getName());
case "float":case "dobule":
return FP_WRAPPERS.contains(argClass.getName());
}
throw new IllegalArgumentException("Unexpected primitive type?!?!: " + primitiveType);
}
private static final HashSet<String> INTEGER_WRAPPERS = new HashSet<>(Arrays.asList(
"java.lang.Integer", "java.lang.Short", "java.lang.Byte", "java.lang.Long"
));
private static final HashSet<String> FP_WRAPPERS = new HashSet<>(Arrays.asList(
"java.lang.Float", "java.lang.Double"
));
private static Class<?>[] getClasses(Object[] oa) {
if (oa == null) return new Class[0];
Class<?>[] ret = new Class[oa.length];
for (int i = 0; i < oa.length; i++) {
ret[i] = oa[i] == null ? Object.class : oa[i].getClass();
}
return ret;
}
}
答案 2 :(得分:0)
经过3个小时的研究和尝试不同的解决方案后,我终于开始工作了。
我使用this answer中的信息(关于构造函数中的原始类型的问题),@ Viktor Sorokin的answer和@ musiKk answer来提问我的问题。我只能通过知道类名/包路径和构造函数参数(作为对象数组)来创建类的实例,能够使用基元,对象,基元数组和对象数组。
代码很长,可以在:/
发布答案 3 :(得分:-1)
执行此操作的简单方法是使用java.beans.Expression.
它包含与编译器相同的所有类型逻辑。只需执行"new MyClass"
形式的字符串表达式,并提供对象数组作为参数。 Expression的值是新对象。手动完成它并不困难,因为你必须为每个参数处理原语和基类。