当前位置: 首页 > 面试题库 >

通过类名称在Java中动态创建对象并通过使用带有数据的列表来设置类字段

尤祖鹤
2023-03-14
问题内容

我有一个包含字符串类型为->的数据的列表 ["classField1", "classField2", "classField3"]

我有一个方法(myMethod(List list, String className))接受列表作为参数。因此,我可以通过参数将此List传递给myMethod(List list,String className)。

在中myMethod,我想创建一个对象,它将是className的实例,即第二个参数。之后,我想通过使用List的数据来设置类的字段。由于我想动态获取类的字段,因此上述结果是我必须将列表的每个String值强制转换为类的每个字段的类型。

我确定列表内的字符串的顺序是正确的,并且与具有相同顺序的类的字段相对应。

有人知道如何执行上述操作吗?

例:

["StringtempValue", "StringUnitOfMeasurement"] =>

创建实例对象:

public class TempStruct {

   private double tempValue;
   private String unitOfMeasurement;

   public TempStruct(double tempValue, String unitOfMeasurement) {
     this.tempValue = tempValue;
     this.unitOfMeasurement = unitOfMeasurement;
   }

}

我尝试通过以下方式给出解决方案:

实际上,我想创建一个现有类的对象,并尝试通过html" target="_blank">反射来实现。我使用以下代码:

Class<?> cls = Class.forName(name);
Object clsInstance = (Object) cls.newInstance();
Field[] objectFields = clsInstance.getClass().getDeclaredFields();

但是当第二行尝试创建新对象时,我遇到了例外。正如@JB Nijet所说,我不知道getDeclaredFields()方法不会返回排序的字段。

实际上,我有一个仅接受字符串列表的方法,因此通过使用反射,我将对象转换为字符串列表,此后,我想做相反的事情。我认为没有其他方法可以做到。


问题答案:

对象的动态实例化可能会变得非常复杂,您的方案涉及以下几个方面:

  • 将对象值从String转换为适当的类型
  • 从类名加载正确的类并创建一个实例
  • 将这些值分配给对象

毫无疑问地将Java视作一种动态语言,对这些要点的全面讨论将占据整整一章。但是,假设您没有时间学习这些错综复杂的内容,或者依赖一些庞大的第三方库,那么我们就来整理一些有助于您解决问题的方法。请随时将手放在车内,以免颠簸。

让我们首先解决类型转换的问题。该值被设置为Strings,但你的对象将它们存储为doublelongint等,所以我们需要解析的功能String到相应的目标类型:

static Object convert(Class<?> target, String s) {
    if (target == Object.class || target == String.class || s == null) {
        return s;
    }
    if (target == Character.class || target == char.class) {
        return s.charAt(0);
    }
    if (target == Byte.class || target == byte.class) {
        return Byte.parseByte(s);
    }
    if (target == Short.class || target == short.class) {
        return Short.parseShort(s);
    }
    if (target == Integer.class || target == int.class) {
        return Integer.parseInt(s);
    }
    if (target == Long.class || target == long.class) {
        return Long.parseLong(s);
    }
    if (target == Float.class || target == float.class) {
        return Float.parseFloat(s);
    }
    if (target == Double.class || target == double.class) {
        return Double.parseDouble(s);
    }
    if (target == Boolean.class || target == boolean.class) {
        return Boolean.parseBoolean(s);
    }
    throw new IllegalArgumentException("Don't know how to convert to " + target);
}

啊。这很丑陋,并且仅处理内部类型。但是我们不是在这里寻求完美,对吧?因此,请适当增强。请注意,从转换String为其他类型实际上是反序列化的一种形式,因此您要对客户(无论给您什么Strings)施加约束,以特定格式提供其值。在这种情况下,格式由parse方法的行为定义。练习1:在将来的某个时候,以向后不兼容的方式更改格式,以引起某人的愤怒。

现在让我们进行实际的实例化:

static Object instantiate(List<String> args, String className) throws Exception {
    // Load the class.
    Class<?> clazz = Class.forName(className);

    // Search for an "appropriate" constructor.
    for (Constructor<?> ctor : clazz.getConstructors()) {
        Class<?>[] paramTypes = ctor.getParameterTypes();

        // If the arity matches, let's use it.
        if (args.size() == paramTypes.length) {

            // Convert the String arguments into the parameters' types.
            Object[] convertedArgs = new Object[args.size()];
            for (int i = 0; i < convertedArgs.length; i++) {
                convertedArgs[i] = convert(paramTypes[i], args.get(i));
            }

            // Instantiate the object with the converted arguments.
            return ctor.newInstance(convertedArgs);
        }
    }

    throw new IllegalArgumentException("Don't know how to instantiate " + className);
}

我们在这里采取了许多捷径,但是,这不是我们正在创建的西斯廷教堂。只需加载该类并搜索其参数数量与参数数量(即arity)匹配的构造函数。重载了相同的构造函数?不,不行。Varargs?不,不行。非公共构造函数?不,不行。而且,如果您不能保证您的类将提供一个像示例一样设置所有字段的构造函数TempStruct,则我将其称为“一天喝啤酒”,因为这种方法是DOA。

找到构造函数后,循环遍历Stringargs将它们转换为构造函数期望的类型。假设可行,然后我们通过反射调用构造函数,挥动魔杖并说abracadabra。Voilà:您有一个新对象。

让我们尝试一个非常人为的例子:

public static void main(String[] args) throws Exception {
    TempStruct ts =
        (TempStruct)instantiate(
            Arrays.asList("373.15", "Kelvin"),
            TempStruct.class.getName());

    System.out.println(
        ts.getClass().getSimpleName() + " " +
        ts.tempValue + " " +
        ts.unitOfMeasurement);
}

输出:

TempStruct 373.15 Kelvin

辉煌



 类似资料:
  • 问题内容: 反射用于加载Java类类并即时对其进行操作。但是我遇到了一个怪异的问题,问我如何通过Reflection快速创建Java类,这意味着这些类在我们要创建之前就没有编译或没有源代码。真的有可能吗?有什么例子吗? 问题答案: 您可以尝试ASM ASM 或字节码工程库 字节码工程库 用于在运行时创建类 在.NET中,我们具有Reflection.Emit(C#)可以执行该 Reflection

  • 基本上,我正在做一个项目,其中某些类属性和类的名称存储在文本文件中。这里的目标是创建文本文件中列出的特定数据类型(在本例中为Car)的对象列表(我已经完成了),然后将这些对象分配给文本文件中的数据类型。下面是我将使用的文本文件的示例: 汽车:2辆 4 1 1红色3 80.5 20 60 2aadawd 1 3 2蓝色3 80 30 20 1aaxzd 自行车: 3 2 1 2号2号 2 3 基本上

  • 问题内容: 我试图遵守MVC惯例,并将所有网络代码保留在我的应用程序中使用的数据服务类中。在一个屏幕上,我有需要显示的用户名和用户名。更新此函数时,我正在调用此函数: 但是尝试返回用户时出现错误!它说: void函数中非预期的非无效返回值。 但是该功能显然不是无效的。那我该怎么办? 问题答案: 您正在将 函数 返回值与Firebase 闭包 返回值混淆-前者是但后者是;) 您实际上是从此关闭返回的

  • 不过就是创建结构体的时候,根据每个对象的特征赋值不同的属性罢了 // 3.创建一个结构体变量 p1 := Person{"lnj", 33} per.say() p2 := Person{"zs", 18} per.Say()

  • 问题内容: 我一直在寻找一个问题:从其字符串名称实例化一个类,该字符串名称描述了如何在具有名称时实例化一个类。有没有办法用Java做到这一点?我将拥有包名称和类名称,并且我需要能够创建具有该特定名称的对象。 问题答案: 两种方式: 方法1-仅适用于具有无参数构造函数的类 如果你的类具有无参数构造函数,则可以使用并使用该方法创建一个实例(尽管请注意,此方法通常被认为是有害的,因为它可以击败Java的

  • 问题内容: 所以我创建了这两个类 现在这是两者的简化版本,但足以说明我的问题。此处显示的类将无法使用,因为找不到四分之一类。为了使其工作,我可以将$ class变量更改为 所以我的问题是:当两个类已经是同一个命名空间的成员时,为什么需要在这里使用命名空间。仅当我将类名放入变量中时,才需要名称空间: 可以正常工作。为什么会这样?在这里有什么需要避免的潜在陷阱吗? 问题答案: 因为可以将字符串从一个名