使用反射将值设置到对象中

发布于 2024-08-30 15:16:57 字数 768 浏览 0 评论 0原文

我有一个具有很多属性的对象,每个属性都有它的 getter 和 setter。每个属性都有一个非原始类型,我在运行时不知道。

例如,我有这样的:

public class a{

    private typeA attr1;
    private typeB attr2;

    public typeA getAttr1(){ return attr1; }
    public typeB getAttr2(){ return attr2; }

    public void setAttr1(typeA at){ attr1 = at; }
    public void setAttr2(typeB at){ attr2 = at; }
}

public class typeA{
    public typeA(){
        // doesn't matter
    }
}

public class typeB{
    public typeB(){
        // doesn't matter
    }
}

因此,使用反射,我获得了属性的setter方法。以标准方式设置值是这样的:

a test = new a();
a.setAttr1(new typeA());

但是如何使用反射来做到这一点?我已经使用反射获得了 setAttr1() 方法,但我不知道如何创建要插入到设置器中的新 typeA 对象。

I have an object that has a lot of attributes, each one with it's getter and setter. Each attribute has a non primitive type, that I don't know at runtime.

For example, what I have is this:

public class a{

    private typeA attr1;
    private typeB attr2;

    public typeA getAttr1(){ return attr1; }
    public typeB getAttr2(){ return attr2; }

    public void setAttr1(typeA at){ attr1 = at; }
    public void setAttr2(typeB at){ attr2 = at; }
}

public class typeA{
    public typeA(){
        // doesn't matter
    }
}

public class typeB{
    public typeB(){
        // doesn't matter
    }
}

So, using reflection, I obtained the setter method for an attribute. Setting a value in the standard way is something like this:

a test = new a();
a.setAttr1(new typeA());

But how can I do this using reflection? I already got the setAttr1() method using reflection, but I don't know how to create a new typeA object to be inserted in the setter.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

雄赳赳气昂昂 2024-09-06 15:16:57

使用 Class#newInstance( )

Class<TypeA> cls = TypeA.class;
TypeA typeA = cls.newInstance();

或者,在您必须确定方法参数的类型的特定情况下:

Class<?> cls = setterMethod.getParameterTypes()[0];
Object value = cls.newInstance();
setterMethod.invoke(bean, value);

您可以在 Sun 关于该主题的教程。也就是说,类名应该以大写开头。我已经在上面的例子中纠正了它。

顺便说一句,您可能会找到提到的工具之一 这里也很有用。

Use Class#newInstance().

Class<TypeA> cls = TypeA.class;
TypeA typeA = cls.newInstance();

Or, in your specific case when you have to determine the type of the method parameter:

Class<?> cls = setterMethod.getParameterTypes()[0];
Object value = cls.newInstance();
setterMethod.invoke(bean, value);

You can learn more about reflection in Sun tutorial on the subject. That said, classnames ought to start with uppercase. I've corrected it in the above example.

By the way, instead of reinventing the Javabean reflection wheel, you may find one of the tools mentioned here useful as well.

荒人说梦 2024-09-06 15:16:57

使用 Class 对象中的 getDeclaredFields() 方法获取所有字段,然后使用 field.set(classInstance, value) 设置值实例中的字段。注意:如果该字段是私有的,您可能必须将该字段上的可访问标志设置为 true。无需依赖 setter 方法。

Use getDeclaredFields() method in the Class object, to get all fields, then use field.set(classInstance, value) to set the value of field in an instance. Note: you may have to set the accessible flag on the field to true, if the field is private. No need to rely on setter methods.

唠甜嗑 2024-09-06 15:16:57

我在做一些事情时遇到了这个。我的总体结论是,每当我觉得我需要一门包含很多领域的课程时,我就做错了。这是我的思考过程:

问题:
- 我需要大量字段来保存这些数据
- 所有这些领域都需要大量的样板文件

解决方案:

  • 使用反射来减少样板文件< “you are here”
  • 使用元数据来指定如何使用字段

新问题:

  • 当新人查看代码时,反射很难理解
  • 一旦你使用元数据足以消除更多样板文件,这些字段通常不会在代码中提及,除了通过元数据——为什么它们是字段?
  • 在代码中指定元数据很快就会变得庞大(顺便说一句,最简单的方法是字符串数组)

解决方案:开始将数据存储在集合中并在外部数据文件中指定元数据

新问题:错误变得很难发现

仔细进行错误检查并且非常明确地显示您的错误消息。确保可能使用您的代码的任何其他程序员都阅读了错误消息。尝试指出元数据何时丢失或错误以及程序员应如何更新元数据 - 包括元数据文件的位置。

问题:没有类型安全

是的,这有时有点烦人。我最终在元数据中包含了类型信息,这样如果有人在字段中输入了错误的值,就可以检测到它——本质上,这将类型安全从构建时转移到运行时,这对我来说很好。

问题:在对象的整个生命周期中重复需要元数据

我不会在每次使用时都按名称查找元数据,而是在开始时解析元数据并将其放入一个对象中(将其称为 IntHolder)。该持有者最终将出现在哈希表中,并且它将包含当前值以及对已解析元数据的引用。

示例

下面是我的元数据最终会出现在样式表的一个字段中的内容:

FieldName:     Age
FieldType      Integer
ScreenBinding: AgeTextField
DBBinding:     User.age
Validation:    IntRange(0, 120); "Age is out of range"

字段名称可能是向用户显示的方式或仅用于您的程序中的方式。一般来说,您不需要通过名称直接操作这种类型的数据——但有时您当然需要这样做。

当您确实需要使用时,请使用 getInt("Age") 和 setInt("Age", 12) 而不是 getAge() 和 setAge(12)——稍微冗长一些,但并不是真正的问题。

如果这是一个问题,您可以创建 getAge/setAge 辅助方法,并且您永远不需要知道它不是一个字段,但这确实会再次开始在样板文件上堆积。

FieldType:指定它的存储方式并让您实现类型检查。

ScreenBinding 和 DBBinding 用于将值复制到其他系统中或从其他系统中复制出来。我还使用这种类型的机制将数据从服务器传输到客户端并返回。

有趣的是验证。当从屏幕上拉出数据时,可以以非常实用的方式将其传递给验证器。在提交到数据库之前可以使用相同的验证器。

验证器可以做的事情远不止这些,它们可以充当触发器(如果值发生变化,则执行此操作)或操作(当用户提交屏幕时,执行此操作)。这些是能够获取值(通过接口)的简单对象——它们可以像您喜欢的那样灵活或强大,但除了通过元数据之外,不直接绑定到任何对象。

这种方法的唯一问题是您必须是喜欢编写固定装置而不是容易出错的样板文件的程序员。 (是的,我发现时间量大致相等,但是当我必须实现样板文件时,我往往会变得非常慢)

在这样做了几次之后,我真的很喜欢这个模式,但是它变得很难实现。下次我这样做时,我将尝试将其变成某种类型的库。

I encountered this on some stuff I was doing. My general conclusion was that whenever I felt like I needed a class with a bunch of fields I was doing it wrong. Here's my thought process:

Problem:
- I need a large number of fields to hold this data
- All these fields require huge amounts of boilerplate

Solution:

  • use reflection to reduce the boilerplate < "you are here"
  • Use metadata to specify how the fields should be used

New Problems:

  • Reflection is difficult to understand when someone new looks at the code
  • Once you go meta enough to eliminate more boilerplate, the fields often have no mention in the code except through the metadata--why are they fields?
  • Specifying the metadata in code becomes bulky quite quickly (easiest way is a string array, by the way)

Solution: Start storing data in a collection and Specify metadata in an external data file

New problem: Errors become hard to find

Be meticulous about error checking and very explicit with your error messages. Make sure any other programmers that might use your code read the error messages. Attempt to indicate when metadata is missing or wrong and how the programmer should update the metdata--include the location of the metadata file.

Problem: No type safety

Yeah, this became somewhat annoying at times. I ended up including type information in the metadata so that if someone put the wrong value in a field, it could be detected--essentially this moves type safety from build time to run time which was fine in my case.

Problem: The metadata is needed repeatedly throughout the life of the object

Rather than looking it up by name every time it's used, I'd parse the metadata at the beginning and put it in an object (call it an IntHolder). This holder would end up in the hashtable and it would contain the current value as well as a reference to the parsed metadata.

Example

Here's what my metadata would end up for one field of a style sheet:

FieldName:     Age
FieldType      Integer
ScreenBinding: AgeTextField
DBBinding:     User.age
Validation:    IntRange(0, 120); "Age is out of range"

The field name might be how it is displayed to the user or just for use in your program. In general, you shouldn't need to directly manipulate this type of data by name--but of course you do sometimes.

When you do need to use, use getInt("Age") and setInt("Age", 12) instead of getAge() and setAge(12)--slightly more verbose but not really a problem.

If it is a problem, you can make getAge/setAge helper methods and you never need to know it's not a field, but that does start piling on the boilerplate again.

FieldType: specifies how it's stored and lets you implement type checking.

ScreenBinding and DBBinding are used to copy the value into and out of other systems. I also used this type of mechanism to transfer the data from server to client and back.

The fun one is Validation. When pulling data off the screen it can be passed to a validator in a very progmatic way. The same validator can be used before committing to the DB.

Validators can do a lot more than that, they can act as triggers (if a value changes, do this) or actions (when user submits a screen, do this). These are a simple objects with the ability to take a value (through an interface)--they can be as flexible or powerful as you like but are not tied directly to any object except through the meta-data.

The only problem with this approach is you have to be the type of programmer that enjoys writing fixtures instead of error-prone boilerplate. (Yes, I find that the amount of time is about equal, but I tend to get really slow when I have to implement boilerplate)

After doing this a few times I really love the pattern, but it gets difficult to implement. Next time I do it I'm going to try to make it into a library of some type.

请你别敷衍 2024-09-06 15:16:57

如果您想在类的每个 setter 中设置一个“新鲜”对象,通常可以通过获取 Method 来实现,对于每个 Method,您都可以获得该类的 Class参数与 getParameterTypes() ,对于每个你调用 Class.newInstance() 的类...并交叉手指(这应该会破坏原始类型 - 我怀疑 Java 是否会自动装箱这里)。
您总是可以询问参数是否是原始调用 isPrimitive()

为什么要为类的原始字段设置“空”实例?它们已经初始化了。您想“重置”它们吗?

If you want to set a "fresh" object in each setter of your class, you can typically do it by getting the Method, for each Method you get the Class of the arguments with getParameterTypes() , for each Class you invoke Class.newInstance() ... and cross your fingers (that should break with primitive types -I doubt Java does autoboxing here).
You can always ask if a parameter is a pimitive calling isPrimitive()

Why would you want to set "empty" instances for primitive fields of a class? They are already initialized. Do you want to "reset" them ?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文