使用反射将值设置为对象

时间:2010-04-28 19:48:56

标签: java reflection setter

我有一个很多属性的对象,每个对象都带有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 对象以插入到setter中。

4 个答案:

答案 0 :(得分: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以大写字母开头。我已经在上面的例子中纠正了它。

顺便说一下,您可能会发现here提到的其中一个工具也很有用,而不是重新发明Javabean反射轮。

答案 1 :(得分:3)

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

答案 2 :(得分:2)

我遇到过一些我正在做的事情。我的一般结论是,每当我觉得我需要一个有一堆字段的课时,我做错了。这是我的思考过程:

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

解决方案:

  • 使用反射来减少样板<&lt; “你在这里”
  • 使用元数据指定字段的使用方式

新问题:

  • 当有人看到代码
  • 时,难以理解反思
  • 一旦你去了meta以消除更多的样板,除了通过元数据之外,这些字段在代码中通常没有提及 - 为什么它们是字段?
  • 在代码中指定元数据会变得非常快(顺便说一下,最简单的方法是字符串数组)

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

新问题:错误变得难以找到

对错误检查一丝不苟,并对您的错误消息非常明确。确保可能使用您的代码的任何其他程序员读取错误消息。尝试指示元数据何时丢失或错误以及程序员应如何更新元数据 - 包括元数据文件的位置。

问题:没有类型安全

是的,有时候这有点令人讨厌。我最终在元数据中包含了类型信息,这样如果有人在字段中输入了错误的值,就可以检测到它 - 基本上这会将类型安全从构建时间移动到运行时间,这在我的情况下很好。

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

不是每次使用时都按名称查找,而是在开头解析元数据并将其放在一个对象中(称之为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用于将值复制到其他系统中和从中复制出来。我还使用这种机制将数据从服务器传输到客户端并返回。

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

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

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

这样做了几次我真的很喜欢这种模式,但实施起来很困难。下次我这样做时,我会尝试将它变成某种类型的库。

答案 3 :(得分:1)

如果要在班级的每个setter中设置一个“新鲜”对象,通常可以通过获取Method来实现,对于每个方法,您获得Class个参数{ {1}},对于每个类调用getParameterTypes() ...并交叉你的手指(应该打破原始类型 - 我怀疑Java在这里做了自动装箱)。 您始终可以询问参数是否为pimitive调用 isPrimitive()

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