开发者

Setting a value into a object using reflection

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 th开发者_高级运维e setter.


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.


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.


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.


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 ?

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜