How can I set or get a field in a class whose name is dynamic and stored in a string variable?
public class Test {
public String a1;
public String a2;
public Test(String key) {
this.key = 'found'; <--- error
}
}
You have to use reflection:
Use Class.getField() to get a Field reference. If it's not public you'll need to call Class.getDeclaredField() instead
Use AccessibleObject.setAccessible to gain access to the field if it's not public
Use Field.set() to set the value, or one of the similarly-named methods if it's a primitive
Here's an example which deals with the simple case of a public field. A nicer alternative would be to use properties, if possible.
import java.lang.reflect.Field;
class DataObject
{
// I don't like public fields; this is *solely*
// to make it easier to demonstrate
public String foo;
}
public class Test
{
public static void main(String[] args)
// Declaring that a method throws Exception is
// likewise usually a bad idea; consider the
// various failure cases carefully
throws Exception
{
Field field = DataObject.class.getField("foo");
DataObject o = new DataObject();
field.set(o, "new value");
System.out.println(o.foo);
}
}
Class<?> actualClass=actual.getClass();
Field f=actualClass.getDeclaredField("name");
The above code would suffice .
object.class.getField("foo");
Unfortunately the above code didn't work for me , since the class had empty field array.
Related
I'm stuck on this problem for almost 3 months now and just can't resolve it myself. I hope it's possible. I'm trying to inject this code with my own custom entity class, which is hard to access, because the class is static and the field is final. Somehow i'm not sure if the generic type is a problem on accessing it.
public class EntityTypes<T extends Entity> {
private final EntityTypes.b<T> aZ;
[some code here]
public interface b<T extends Entity> {
T create(EntityTypes<T> entitytypes, World world);
}
public static class a<T extends Entity> {
private final EntityTypes.b<T> a;
[more code here]
}
}
So far i tried to use Reflections, but i keep getting:
java.lang.IllegalArgumentException: Can not set net.server.EntityTypes$b field net.server.EntityTypes$a.a to net.server.EntityTypes
That is my running code:
// works
ReflectionUtils.setFinal(EntityTypes.class, EntityTypes.VILLAGER, "aZ", (EntityTypes.b<CustomVillager>) CustomVillager::new);
// while this does not work!
ReflectionUtils.setFinal(EntityTypes.a.class, EntityTypes.VILLAGER, "a", (EntityTypes.b<CustomVillager>) CustomVillager::new);
public class ReflectionUtils {
// Does only work on Java 12 and above!!
public static void setFinal(Class cls, Object obj, String fieldName, Object value) {
try {
Field field = cls.getDeclaredField(fieldName);
FieldHelper.makeNonFinal(field);
field.setAccessible(true);
field.set(obj, value);
} catch (Exception e) {
e.printStackTrace();
}
}
// For Java 12 final field injection
// https://stackoverflow.com/questions/56039341/get-declared-fields-of-java-lang-reflect-fields-in-jdk12/
public final static class FieldHelper {
private static final VarHandle MODIFIERS;
static {
try {
var lookup = MethodHandles.privateLookupIn(Field.class, MethodHandles.lookup());
MODIFIERS = lookup.findVarHandle(Field.class, "modifiers", int.class);
} catch (IllegalAccessException | NoSuchFieldException ex) {
throw new RuntimeException(ex);
}
}
public static void makeNonFinal(Field field) {
int mods = field.getModifiers();
if (Modifier.isFinal(mods)) {
MODIFIERS.set(field, mods & ~Modifier.FINAL);
}
}
}
}
public class CustomVillager extends EntityVillager {
public CustomVillager(EntityTypes<? extends CustomVillager> entityTypes, World world) {
super(entityTypes, world);
}
}
The exception you are getting means that the Field object represents a field on a class that is different than the class of the the object you are trying to set it on. So in your setFinal() method, you get a Field object representing the field named fieldName on the class cls, and then you try to set that field on the object obj. That means that the object passed in as obj must be an instance of the class cls, or otherwise it won't work.
Looking at the two lines that call setFinal(), the first gets the field aZ in EntityTypes class; this field only exists on an instance of EntityTypes. The second setFinal() call gets the field a in the EntityTypes.a class; this field only exists on an instance of EntityTypes.a. You try to set both of these fields on EntityTypes.VILLAGER. You have not shown the code that declares or initializes EntityTypes.VILLAGER, so we don't know what it is, but these two lines would only work if EntityTypes.VILLAGER were both an instance of EntityTypes and an instance of EntityTypes.a, which is impossible (since they are both classes, neither is a subclass of the other, and Java does not have double inheritance of classes). So one of these two lines must be wrong.
How can I set or get a field in a class whose name is dynamic and stored in a string variable?
public class Test {
public String a1;
public String a2;
public Test(String key) {
this.key = 'found'; <--- error
}
}
You have to use reflection:
Use Class.getField() to get a Field reference. If it's not public you'll need to call Class.getDeclaredField() instead
Use AccessibleObject.setAccessible to gain access to the field if it's not public
Use Field.set() to set the value, or one of the similarly-named methods if it's a primitive
Here's an example which deals with the simple case of a public field. A nicer alternative would be to use properties, if possible.
import java.lang.reflect.Field;
class DataObject
{
// I don't like public fields; this is *solely*
// to make it easier to demonstrate
public String foo;
}
public class Test
{
public static void main(String[] args)
// Declaring that a method throws Exception is
// likewise usually a bad idea; consider the
// various failure cases carefully
throws Exception
{
Field field = DataObject.class.getField("foo");
DataObject o = new DataObject();
field.set(o, "new value");
System.out.println(o.foo);
}
}
Class<?> actualClass=actual.getClass();
Field f=actualClass.getDeclaredField("name");
The above code would suffice .
object.class.getField("foo");
Unfortunately the above code didn't work for me , since the class had empty field array.
I met a problem that is , when I use smart-json to convert java bean to JSON Object, then i found some boolean filed was lost, can some boby tell me why? the who codes are below:
package com.huoli.crawler.test;
import java.util.HashMap;
import java.util.Map;
import net.minidev.json.JSONObject;
public class MiniDevJSONTest {
public static void main(String[] args) {
MyBean mybean = new MyBean();
mybean.setReturn(true);
mybean.setArrivingAirportCode("dadsa");
Map<String, MyBean> map = new HashMap<>();
map.put("mybean", mybean);
// output smart-json:{"mybean":{"arrivingAirportCode":"dadsa"}}
// so where is isRetrun ??
System.out.println("smart-json:" + JSONObject.toJSONString(map));
}
}
class MyBean {
private boolean isReturn;
public boolean isReturn() {
return isReturn;
}
public void setReturn(boolean isReturn) {
this.isReturn = isReturn;
}
private String arrivingAirportCode;
public String getArrivingAirportCode() {
return arrivingAirportCode;
}
public void setArrivingAirportCode(String arrivingAirportCode) {
this.arrivingAirportCode = arrivingAirportCode;
}
}
My question is why the boolean field value was lost?
This is a getter versus "is"-er problem:
Modify your code in MyBean as I have the following snippet, and change the set of the boolean in your MiniDevJSONTest class to match "setIsReturn". You will now get the value you are looking for. Not very familiar with the minidev.json classes, but there appears to be reflection going on underneath the covers that is looking for the getter for your boolean value and not the "is"-er. Since it doesn't find it, it's like it's not there..
I have seen this kind of behavior before in other libraries. In some libraries the choice between whether the code seeks out the is-er or the getter changes based on whether or not the boolean you are looking for is the primitive or the fully boxed type.
public class MyBean {
private boolean isReturn;
private String arrivingAirportCode;
public boolean getIsReturn() {
return isReturn;
}
public void setIsReturn(boolean isReturn) {
this.isReturn = isReturn;
}
public String getArrivingAirportCode() {
return arrivingAirportCode;
}
public void setArrivingAirportCode(String arrivingAirportCode) {
this.arrivingAirportCode = arrivingAirportCode;
}
}
Just a little more followup:
In eclipse, when you establish a class attribute for a class, if you use the "create getter and setter" shortcut, you will see that it automatically creates a getter for a big B Boolean, and a is-er for the primitive type.
Many libraries use this standard when trying to figure out the reflection pattern for examining a class. However, it appears that the library you are using does not. I tested it, and it is expecting the getter whether the attribute is a boxed type or the primitive.
I want to create something that resembles an extendable Enum (understanding extending Enums isn't possible in Java 6).
Here is what im trying to do:
I have many "Model" classes and each of these classes have a set of Fields that are to be associated with it. These Fields are used to index into Maps that contain representations of the data.
I need to be able to access the Fields from an Class OR instance obj as follows:
MyModel.Fields.SOME_FIELD #=> has string value of "diff-from-field-name"
or
myModel.Fields.SOME_FIELD #=> has string value of "diff-from-field-name"
I also need to be able to get a list of ALL the fields for Model
MyModel.Fields.getKeys() #=> List<String> of all the string values ("diff-from-field name")
When defining the "Fields" class for each Model, I would like to be able to keep the definition in the same file as the Model.
public class MyModel {
public static final Fields extends BaseFields {
public static final String SOME_FIELD = "diff-from-field-name";
public static final String FOO = "bar";
}
public Fields Fields = new Fields();
// Implement MyModel logic
}
I also want to have OtherModel extends MyModel and beable to inherit the Fields from MyModel.Fields and then add its own Fields on top if it ..
public class OtherModel extends MyModel {
public static final class Fields extends MyModel.Fields {
public static final String CAT = "feline";
....
Which woulds allow
OtherModel.Fields.CAT #=> feline
OtherModel.Fields.SOME_FIELD #=> diff-from-field-name
OtherModel.Fields.FOO #=> bar
OtherModel.Fields.getKeys() #=> 3 ["feline", "diff-from-field-name", "bar"]
I am trying to make the definition of the "Fields" in the models as clean and simple as possible as a variety of developers will be building out these "Model" objects.
Thanks
I need to be able to access the Fields from an Class OR instance obj as follows:
MyModel.Fields.SOME_FIELD #=> has string value of "diff-from-field-name"
That is not possible in Java unless you use a real enum or SOME_FIELD is a real field. In either case, the "enum" is not extensible.
The best you can do in Java 6 is to model the enumeration as mapping from String names to int values. That is extensible, but the mapping from names to values incurs a runtime cost ... and the possibility that your code will use a name that is not a member of the enumeration.
The reason that enum types in Java are not extensible is that the extended enum would break the implicit invariants of the original enum and (as a result) could not be substitutable.
I've just tried out some code trying to do what you've just described and it was really cumbersome.
If you have a Fields static inner class somewhere in a model class like this:
public class Model {
public static class Fields {
public static final String CAT = "cat";
protected static final List<String> KEYS = new ArrayList<String>();
static {
KEYS.add(CAT);
}
protected Fields() {}
public static List<String> getKeys() {
return Collections.unmodifiableList(KEYS);
}
}
}
and you extend this class like this:
public class ExtendedModel extends Model {
public static class ExtendedFields extend Model.Fields {
public static final String DOG = "dog";
static {
KEYS.add(DOG);
}
protected ExtendedFields() {}
}
}
then its just wrong. If you call Model.Fields.getKeys() you'd get what you expect: [cat], but if you call ExtendedModel.ExtendedFields.getKeys() you'd get the same: [cat], no dog. The reason: getKeys() is a static member of Model.Fields calling ExtendedModel.ExtendedFields.getKeys() is wrong because you really call Model.Fields.getKeys() there.
So you either operate with instance methods or create a static getKeys() method in all of your Fields subclasses, which is so wrong I can't even describe.
Maybe you can create a Field interface which your clients can implement and plug into your model(s).
public interface Field {
String value();
}
public class Model {
public static Field CAT = new Field() {
#Override public String value() {
return "cat";
}
};
protected final List<Field> fields = new ArrayList();
public Model() {
fields.add(CAT);
}
public List<Field> fields() {
return Collections.unmodifiableList(fields);
}
}
public class ExtendedModel extends Model {
public static Field DOG= new Field() {
#Override public String value() {
return "dog";
}
};
public ExtendedModel() {
fields.add(DOG);
}
}
I wonder whether you really need a generated enumeration of fields. If you are going to generate a enum of a list the fields based on a model, why not generate a class which lists all the fields and their types? i.e. its not much harder to generate classes than staticly or dynamically generated enums and it much more efficient, flexible, and compiler friendly.
So you could generate from a model something like
class BaseClass { // with BaseField
String field;
int number;
}
class ExtendedClass extends BaseClass { // with OtherFields
String otherField;
long counter;
}
Is there a real benefit to inventing your own type system?
I was able to come up with a solution using reflection that seems to work -- I haven't gone through the full gamut of testing, this was more me just fooling around seeing what possible options I have.
ActiveField : Java Class which all other "Fields" Classes (which will be inner classes in my Model classes) will extend. This has a non-static method "getKeys()" which looks at "this's" class, and pulled a list of all the Fields from it. It then checks a few things like Modifiers, Field Type and Casing, to ensure that it only looks at Fields that match my convention: all "field keys" must be "public static final" of type String, and the field name must be all UPPERCASE.
public class ActiveField {
private final String key;
protected ActiveField() {
this.key = null;
}
public ActiveField(String key) {
System.out.println(key);
if (key == null) {
this.key = "key:unknown";
} else {
this.key = key;
}
}
public String toString() {
return this.key;
}
#SuppressWarnings("unchecked")
public List<String> getKeys() {
ArrayList<String> keys = new ArrayList<String>();
ArrayList<String> names = new ArrayList<String>();
Class cls;
try {
cls = Class.forName(this.getClass().getName());
} catch (ClassNotFoundException e) {
return keys;
}
Field fieldList[] = cls.getFields();
for (Field fld : fieldList) {
int mod = fld.getModifiers();
// Only look at public static final fields
if(!Modifier.isPublic(mod) || !Modifier.isStatic(mod) || !Modifier.isFinal(mod)) {
continue;
}
// Only look at String fields
if(!String.class.equals(fld.getType())) {
continue;
}
// Only look at upper case fields
if(!fld.getName().toUpperCase().equals(fld.getName())) {
continue;
}
// Get the value of the field
String value = null;
try {
value = StringUtils.stripToNull((String) fld.get(this));
} catch (IllegalArgumentException e) {
continue;
} catch (IllegalAccessException e) {
continue;
}
// Do not add duplicate or null keys, or previously added named fields
if(value == null || names.contains(fld.getName()) || keys.contains(value)) {
continue;
}
// Success! Add key to key list
keys.add(value);
// Add field named to process field names list
names.add(fld.getName());
}
return keys;
}
public int size() {
return getKeys().size();
}
}
Then in my "Model" classes (which are fancy wrappers around a Map, which can be indexed using the Fields fields)
public class ActiveResource {
/**
* Base fields for modeling ActiveResource objs - All classes that inherit from
* ActiveResource should have these fields/values (unless overridden)
*/
public static class Fields extends ActiveField {
public static final String CREATED_AT = "node:created";
public static final String LAST_MODIFIED_AT = "node:lastModified";
}
public static final Fields Fields = new Fields();
... other model specific stuff ...
}
I can then make a class Foo which extends my ActiveResource class
public class Foo extends ActiveResource {
public static class Fields extends ActiveResource.Fields {
public static final String FILE_REFERENCE = "fileReference";
public static final String TYPE = "type";
}
public static final Fields Fields = new Fields();
... other Foo specific stuff ...
Now, I can do the following:
ActiveResource ar = new ActiveResource().
Foo foo = new Foo();
ar.Fields.size() #=> 2
foo.Fields.size() #=> 4
ar.Fields.getKeys() #=> ["fileReference", "type", "node:created", "node:lastModified"]
foo.Fields.getKeys() #=> ["node:created", "node:lastModified"]
ar.Fields.CREATED_AT #=> "node:created"
foo.Fields.CREATED_AT #=> "node:created"
foo.Fields.TYPE #=> "type"
etc.
I can also access the Fields as a static field off my Model objects
Foo.Fields.size(); Foo.Fields.getKeys(); Foo.Fields.CREATED_AT; Foo.Fields.FILE_REFERENCE;
So far this looks like a pretty nice solution, that will require minimal instruction for building out new Models.
Curses - For some reason my very lengthy response with the solution i came up with did not post.
I will just give a cursory overview and if anyone wants more detail I can re-post when I have more time/patience.
I made a java class (called ActiveField) from which all the inner Fields inherit.
Each of the inner field classes have a series of fields defined:
public static class Fields extends ActiveField {
public static final String KEY = "key_value";
}
In the ActiveRecord class i have a non-static method getKeys() which uses reflection to look at the all the fields on this, iterates through, gets their values and returns them as a List.
It seems to be working quite well - let me know if you are interested in more complete code samples.
Is it possible to get the class type from inside the static initialization block?
This is a simplified version of what I currently have::
class Person extends SuperClass {
String firstName;
static{
// This function is on the "SuperClass":
// I'd for this function to be able to get "Person.class" without me
// having to explicitly type it in but "this.class" does not work in
// a static context.
doSomeReflectionStuff(Person.class); // IN "SuperClass"
}
}
This is closer to what I am doing, which is to initialize a data structure that holds information about the object and its annotations, etc... Perhaps I am using the wrong pattern?
public abstract SuperClass{
static void doSomeReflectionStuff( Class<?> classType, List<FieldData> fieldDataList ){
Field[] fields = classType.getDeclaredFields();
for( Field field : fields ){
// Initialize fieldDataList
}
}
}
public abstract class Person {
#SomeAnnotation
String firstName;
// Holds information on each of the fields, I used a Map<String, FieldData>
// in my actual implementation to map strings to the field information, but that
// seemed a little wordy for this example
static List<FieldData> fieldDataList = new List<FieldData>();
static{
// Again, it seems dangerous to have to type in the "Person.class"
// (or Address.class, PhoneNumber.class, etc...) every time.
// Ideally, I'd liken to eliminate all this code from the Sub class
// since now I have to copy and paste it into each Sub class.
doSomeReflectionStuff(Person.class, fieldDataList);
}
}
Edit
I picked the accepted answer based on what applied best to my problem, however it seems to me that all three of the current answers have their merits.
No, it's not possible without grabbing the stacktrace (which is imo nastier than your initial approach and for which I would in any way prefer Thread#getStackTrace() above new Exception()).
Rather do that job in a non-static initializer (or the default constructor) of the abstract class where you check the initialized status.
public abstract class SuperClass {
{
if (!isInitialized(getClass())) {
initialize(getClass());
}
}
}
The called methods in turn can be safely static.
yes, I use this often to initialize a static Log variable :
e.g. :
public class Project implements Serializable, Cloneable, Comparable<Project> {
private static final Logger LOG = LoggerFactory.getLogger(Project.class);
...
To get a class at runtime, you could do something along the lines of
public class Test {
public static void main(String[] args) {
try{
throw new Exception();
}
catch(Exception e){
StackTraceElement[] sTrace = e.getStackTrace();
// sTrace[0] will be always there
String className = sTrace[0].getClassName();
System.out.println(className);
}
}
}
Not pretty but will do the job (ripped from http://www.artima.com/forums/flat.jsp?forum=1&thread=155230).
This means you still make a call from the subclass (so is in the stack trace), but you don't need to include the XXX.class as an argument.