2010-04-28 50 views
5

我有一个对象,它具有很多属性,每个属性都有它的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 
    } 
} 

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

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

但是我怎样才能使用反射?我已经得到了setAttr1()方法使用反射,但我不知道如何创建一个新的typeA对象将被插入设置。

回答

11

使用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 tutorial on the subject。也就是说,类名ought以大写字母开头。我在上面的例子中纠正了它。顺便说一句,与其重新创建Javabean反射轮,您可能会发现其中一个工具here也很有用。

+2

它们必须有一个默认的(无参数)构造函数(+1) – Bozho 2010-04-28 19:53:29

1

如果你想设置一个“新鲜”的对象在你的类的每一个二传手,你通常可以通过获取Method做到这一点,对于每一个方法,你得到的参数与getParameterTypes()Class,为每个类调用Class.newInstance() ...并交叉你的手指(这应该与原始类型打破 - 我怀疑Java在这里自动装箱)。 您可以随时问,如果一个参数是pimitive调用isPrimitive()

你为什么要设置一类原始的领域“空”的情况?它们已经初始化。你想“重置”它们吗?

+0

您可以使用int.class,boolean.class等搜索使用原语的方法或Integer.TYPE ... – pyb 2015-01-23 18:51:43

3

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

+2

这是不好的建议,因为你绕过了制定者。即使现在并不重要,它可能迟一点,因为这是反思,没有人会知道。 – Yishai 2010-04-28 21:03:33

+1

好吧,反思已经是黑魔法,整点是让你打破书中的每一条规则。如果他正在构建某种注入机制,那么没有理由不直接注入,J2EE会这样做(@PersistenceContext) – 2010-04-28 22:01:16

2

我在做某些事情时遇到过这个问题。我的一般结论是,每当我觉得我需要一个领域的班级,我做错了。这是我的思维过程:

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

解决方案:

  • 使用反射来降低样板<“你在这里”
  • 使用元数据指定字段应如何使用

新问题:

  • 反思是很难理解,当有人以全新的面貌在代码
  • 一旦你足够的元,以消除更多的样板,该领域往往在代码中没有提及,除非通过metadata-他们为什么是田地?
  • 指定在码元数据变成庞大的相当快(最简单的方式是一个字符串数组,顺便)

解决方案:启动一个集合中的存储数据和在外部数据文件指定元数据

新问题:错误很难找到

请谨慎对待错误检查,并非常明确地表明您的错误消息。确保其他可能使用您的代码的程序员阅读错误消息。尝试指示元数据何时缺失或错误以及程序员应如何更新metdata - 包括元数据文件的位置。

问题:无类型安全

呀,这成了时代有些烦人。我最终在元数据中包含了类型信息,这样如果有人在字段中输入了错误的值,可能会发现它 - 从本质上讲,这会将类型安全从构建时间移动到运行时间,这对我来说是很好的。

问题:重复所需的元数据在整个对象的生命

而不是每次它的使用时间寻找它的名字,我会解析开头的元数据,并把它放在一个对象(电话它是一个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用于将值复制到其他系统和从其他系统中复制出来。我也使用这种机制将数据从服务器传输到客户端并返回。

有趣的是验证。当从屏幕上提取数据时,它可以以非常有计划的方式传递给验证器。提交到数据库之前可以使用相同的验证器。

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

这种方法的唯一问题是你必须是喜欢书写夹具而不是容易出错的样板的程序员。 (是的,我发现时间大致相等,但当我不得不实施样板时,我倾向于变得非常慢)

经过这样的几次我真的很喜欢这个模式,但它很难实现。下次我这样做时,我会尝试将它变成某种类型的库。