Cast generic object to generic class - java

I would like to pass in a generic object into my method and have it get the property name, type, and value.
Here is my class
public class Login {
public String token;
public String customerid;
public Class1 class1;
public Class2 class2;
public class Class1 {
public Class3 class3;
public String string1;
public class Class3 {
public int int1;
public String string2;
public String string3;
}
}
public class Class2 {
public int int1;
public String string2;
public String string3;
}
}
I would like the output to look like this
User Preferences customerid - class java.lang.String - 586969
User Preferences token - class java.lang.String - token1
User Preferences string1 - class java.lang.String - string1Value
User Preferences string2 - class java.lang.String - string2Value
User Preferences string3 - class java.lang.String - string3Value
The code I have right now gives me issues. Here is the code:
try {
// Loop over all the fields and add the info for each field
for (Field field : obj.getClass().getDeclaredFields()) {
if(!field.isSynthetic()){
field.setAccessible(true);
System.out.println("User Preferences " + field.getName() + " - " + field.getType() + " - " + field.get(obj));
}
}
// For any internal classes, recursively call this method and add the results
// (which will in turn do this for all of that subclass's subclasses)
for (Class<?> subClass : obj.getClass().getDeclaredClasses()) {
Object subObject = subClass.cast(obj); // ISSUE
addUserPreferences(subObject, prefs);
}
}catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}catch(ClassCastException e) {
e.printStackTrace();
}
Getting the subObject, in this case Class1 or Class2, and passing it to the method is what Im having an issue with. I have tried with a class instead of an object but then I can't get the object from the class.
Is there anyway to cast the object I pass in to the subclass?
Thanks

You have a few options:
One option is to consider defining some interface that defines an object that provides user preferences, e.g.:
interface UserPreferenceProvider {
Map<String,Object> getUserPrefences();
}
Then you can make your classes implement that interface, e.g.:
public class Login implements UserPreferenceProvider {
...
public class Class1 implements UserPreferenceProvider {
...
public class Class2 implements UserPreferenceProvider {
...
}
}
}
Where their getUserPreferences() implementations return the preferences to write.
Then you can change addUserPreferences() to take a UserPreferenceProvider, and when you are traversing fields, check if you find a UserPreferenceProvider and, if so, cast it to that and pass it off to addUserPreferences().
This would more accurately represent your intentions, as well. I believe the fundamental issue here is you have these arbitrary objects that you're trying to work with, and while conceptually they have something in common, your code is not representing that concept; I know that's a bit vague but by not having your code reflect that, you are now faced with the awkward task of having to find a way to force your arbitrary objects to be treated in a common way.
A second option could be to create a custom annotation, e.g. #UserPreference, and use that to mark the fields you want to write. Then you can traverse the fields and when you find a field with this annotation, add it's single key/value to the user preferences (that is, operate on the fields themselves, instead of passing entire container classes to addUserPreferences()).
This may or may not be more appropriate than the first option for your design. It has the advantage of not forcing you to use those interfaces, and not having to write code to pack data into maps or whatever for getUserPreferences(); it also gives you finer grained control over which properties get exported -- essentially this shifts your focus from the objects to the individual properties themselves. It would be a very clean approach with minimal code.
A possible way to make this more convenient if you already have bean-style getters is to use e.g. Apache BeanUtils to get the values instead of rolling your own; but for your situation it's a pretty basic use of reflection that may not be worth an additional dependency.
Here is an example of getting names and values of the fields of an object tagged with a custom annotation. A second annotation is used to mark fields that contain objects that should be recursively descended into and scanned. It's very straightforward:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
// #UserPreference marks a field that should be exported.
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
#interface UserPreference {
}
// #HasUserPreferences marks a field that should be recursively scanned.
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
#interface HasUserPreferences {
}
// Your example Login class, with added annotations.
class Login {
#UserPreference public String token; // <= a preference
#UserPreference public String customerid; // <= a preference
#HasUserPreferences public Class1 class1; // <= contains preferences
public class Class1 {
#HasUserPreferences public Class2 class2; // <= contains preferences
#UserPreference public String string1; // <= a preference
public class Class2 {
public int int1; // <= not a preference
#UserPreference public String string2; // <= a preference
#UserPreference public String string3; // <= a preference
}
}
// Construct example:
public Login () {
token = "token1";
customerid = "586969";
class1 = new Class1();
class1.string1 = "string1Value";
class1.class2 = class1.new Class2();
class1.class2.string2 = "string2Value";
class1.class2.string3 = "string3Value";
}
}
public class ValueScanExample {
// Recursively print user preferences.
// Fields tagged with #UserPreference are printed.
// Fields tagged with #HasUserPreferences are recursively scanned.
static void printUserPreferences (Object obj) throws Exception {
for (Field field : obj.getClass().getDeclaredFields()) {
// Is it a #UserPreference?
if (field.getAnnotation(UserPreference.class) != null) {
String name = field.getName();
Class<?> type = field.getType();
Object value = field.get(obj);
System.out.println(name + " - " + type + " - " + value);
}
// Is it tagged with #HasUserPreferences?
if (field.getAnnotation(HasUserPreferences.class) != null) {
printUserPreferences(field.get(obj)); // <= note: no casts
}
}
}
public static void main (String[] args) throws Exception {
printUserPreferences(new Login());
}
}
The output is:
token - class java.lang.String - token1
customerid - class java.lang.String - 586969
string2 - class java.lang.String - string2Value
string3 - class java.lang.String - string3Value
string1 - class java.lang.String - string1Value
Note that "int1" is not present in the output, as it is not tagged. You can run the example on ideone.
The original basic annotation example can still be found here.
You can do all sorts of fun things with annotations, by the way, e.g. add optional parameters that let you override the field name in the preferences, add a parameter that lets you specify a custom object -> user preference string converter, etc.

I have figured out a simplistic way to do this. Anyone who has suggestions to make this better or has issues with the code please comment. The code below does work for me
try {
Class<?> objClass = obj.getClass();
List<Object> subObjectList = new ArrayList<Object>();
// Loop over all the fields and add the info for each field
for (Field field: objClass.getDeclaredFields()) {
if(!field.isSynthetic()){
if(isWrapperType(field.getType())){
System.out.println("Name: " + field.getName() + " Value: " + field.get(obj));
}
else{
if(field.getType().isArray()){
Object[] fieldArray = (Object[]) field.get(obj);
for(int i = 0; i < fieldArray.length; i++){
subObjectList.add(fieldArray[i]);
}
}
else{
subObjectList.add(field.get(obj));
}
}
}
}
for(Object subObj: subObjectList){
printObjectFields(subObj);
}
}catch(IllegalArgumentException e){
// TODO Auto-generated catch block
e.getLocalizedMessage();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.getLocalizedMessage();
}
The isWrapperType come from code I found in this stack overflow question. All i did was add String and int to the set.

Related

Java class method with Class and Field as parameters

How can i create a method that accepts Class and Field as parameters? Like this:
List<SomeClassEntity> list = ...;
// Service to make useful things around a list of objects
UsefulThingsService<SomeClassEntity> usefulThingsService = new UsefulThingsService<>();
// Maybe invoke like this. Did't work
usefulThingsService.makeUsefulThings(list, SomeClassEntity.class, SomeClassEntity::getFieldOne);
// or like this. Will cause delayed runtime erros
usefulThingsService.makeUsefulThings(list, SomeClassEntity.class, "fieldTwo");
public class SomeClassEntity {
Integer fieldOne = 10;
Double fieldThree = 0.123;
public Integer getFieldOne() {
return fieldOne;
}
public void setFieldOne(Integer fieldOne) {
this.fieldOne = fieldOne;
}
public Double getFieldThree() {
return fieldThree;
}
public void setFieldThree(Double fieldThree) {
this.fieldThree = fieldThree;
}
}
public class UsefulThingsService<T> {
public void makeUsefulThings(Class<T> someClassBClass, String fieldName) {
// there is some code
}
}
Want to have correct references on compile stage, not at runtime.
Update:
I need code that would look more convenient than this:
Field fieldOne = null;
try {
fieldOne = SomeClassEntity.class.getDeclaredField("fieldOne");
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
usefulThingsService.makeUsefulThings(SomeClassEntity.class, fieldOne);
I apologize for the next clarification.
Update 2:
- The service compares the list with the previous list, reveals only the changed fields of objects (list items) and updates these fields in the objects in the original list.
- Currently i use annotation on entity's field that is actually ID of the entity and that ID is used to detect identically entities (old and new) when i need to update field of entity in source list.
- Service detect annotated field and use it for next update process.
- I want to refuse to use annotations and provide an Field directly in constructor of service. Or use something other that could establish a relationship between class and field on compilation stage.
Assuming that you want field access because you want to get and set the value, you’d need two functions:
public class UsefulThingsService<T> {
public <V> void makeUsefulThings(List<T> list, Function<T,V> get, BiConsumer<T,V> set) {
for(T object: list) {
V v = get.apply(object);
// there is some code
set.accept(object, v);
}
}
}
and
usefulThingsService.makeUsefulThings(
list, SomeClassEntity::getFieldOne, SomeClassEntity::setFieldOne);
usefulThingsService.makeUsefulThings(
list, SomeClassEntity::getFieldThree, SomeClassEntity::setFieldThree);
There are, however, some things open. E.g., how is this service supposed to do something useful with the field resp. property, without even knowing its actual type. In your example, both are subtypes of Number, so you could declare <V extends Number>, so the method knows how to extract numerical values, however, constructing an appropriate result object would require specifying another function argument.

How to access a field within a static inner class with a generic type? (very difficult)

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 to get values of nested class attribute using reflection?

Currently I have classes with several nested classes inside is, like:
public class Foo {
private Bar bar;
//getter & setter
public class Bar {
private Abc abc;
//getter & setter
public class Abc {
#RequiredParam
private String property;
//getter & setter
}
}
}
I am trying to get the value of the fields but I am having a hard time how to achieve this.
So far I have:
public static boolean isValid(Object paramClazz) throws Exception {
List<Class> classes = new ArrayList<>();
getClasses(classes, paramClazz.getClass());
for (Class clazz : classes) {
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(RequiredParam.class)) {
field.setAccessible(true);
//how to get the value? field.get(paramClazz) doesn't work
}
}
}
return true;
}
private static void getClasses(List<Class> classes, Class<?> clazz) {
if (clazz.getDeclaredClasses().length > 0) {
for (Class<?> c : clazz.getDeclaredClasses()) {
getClasses(classes, c);
}
}
classes.add(clazz);
}
My goal is to the able to check if the field annotated with #RequiredParam is not null, so I have the method isValid() which will received an object and should be able to check all fields (even the ones inside nested classes) and see if any is missing.
The problem is when I try to call field.get() and I don't know which object I am supposed to pass to this method. Passing the highest level object won't work, because I need somehow to pass only the Abc object to the method.
How can I get the correct object to pass to the field.get() call, considering I can have more or less nested levels in my classes?
This is an example code that scans all fields of an object recursively until it finds values of all annotated fields:
public static Collection<Object> getAnnotatedValues(final Object root) throws ReflectiveOperationException {
return getAnnotatedValues(root, new HashSet<>());
}
private static Collection<Object> getAnnotatedValues(final Object root, final Set<Object> inspected)
throws ReflectiveOperationException {
final List<Object> annotatedValues = new ArrayList<>();
if (inspected.contains(root)) { // Prevents stack overflow.
return Collections.EMPTY_LIST;
}
inspected.add(root);
for (final Field field : gatherFields(root.getClass())) {
field.setAccessible(true);
final Object currentValue = field.get(root);
field.setAccessible(false);
if (field.isAnnotationPresent(RequiredParam.class)) {
// Found required value, search finished:
annotatedValues.add(currentValue);
if (currentValue != null) {
inspected.add(currentValue);
}
} else if (currentValue != null) {
// Searching for annotated fields in nested classes:
annotatedValues.addAll(getAnnotatedValues(currentValue, inspected));
}
}
return annotatedValues;
}
private static Iterable<Field> gatherFields(Class<?> fromClass) {
// Finds ALL fields, even the ones from super classes.
final List<Field> fields = new ArrayList<>();
while (fromClass != null) {
fields.addAll(Arrays.asList(fromClass.getDeclaredFields()));
fromClass = fromClass.getSuperclass();
}
return fields;
}
You might have implemented something similar, but had trouble getting to the last nested class. This is because Field instance is just a description of a class and (unless the field is static) it needs an actual instance the be able to extract a value. From Field#get(Object) method docs:
If the underlying field is a static field, the obj argument is ignored; it may be null.
Otherwise, the underlying field is an instance field. If the specified obj argument is null, the method throws a NullPointerException. If the specified object is not an instance of the class or interface declaring the underlying field, the method throws an IllegalArgumentException.
If you did not initiate fields in your class structure, you would have a hard time extracting a value - even if you found annotated fields, you would still need an instance of the class to extract them. For example, given these classes:
public class Foo {
private final Bar bar = new Bar();
public class Bar {
private final Abc abc = new Abc();
public class Abc {
#RequiredParam private final String property = "Result.";
}
}
#Retention(RetentionPolicy.RUNTIME)
public static #interface RequiredParam {}
}
...getAnnotatedValues(new Foo()) returns collection containing "Result.". You can easily modify the methods to fit your needs (for example, return true as soon as the first valid annotated field is found or simply return false if the collection is empty/contains nulls).

Using Java Reflection to determine which class to instantiate

There is a Message superclass and there are various Message subclasses like WeddingMessage, GreetingMessage, FarewellMessage, Birthday Message.
The Message superclass has a constructor:
public Message(String messageType){
this.messageType = messageType;
}
The message subclasses all have different constructors, but they all make a call to the superclass, where they pass the messageType as an argument So for example:
public BirthdayMessage( String name, int age){
super("birthday");
System.out.println("Happy birthday " + name + "You are " + age " years old");
public FareWellMessage(String name, String message){
super("farewell");
System.out.println(message + " " + name);
}
The messageType which is created is determined by arguments passed in by the user. So for example, if a user inserts 'birthday John 12', then a BirthdayMessage will be created with parameters John and 12. If a user enters 'farewell Grace take care' then an instance of FarewellMessage is created with those parameters.
Instead of having a bunch of if/else statements or a switch case, in the form of something like-
words[] = userinput.slice(' ');
word1 = words[0];
if (word1 == birthday)
create new BirthdayMessage(parameters here)
if (word1 == wedding)
create new weddingMessage(parameters here)
etc
How could i use reflection to determine which type of Message class to create.
My current idea is to use the File class to get all the Files in the package which contain the message subclasses. Then use reflection to get each of their constructor parameter types and see if they match the parameters given by user input. Then make instances of those matching classes with random parameters. When made, the subclass will make a call to its superclass constructor with its messageType. Then i can check to see if the messageType variable matches the user input.
So if the user enters 'birthday john 23'
I find all constructors in the package that take a String and an int as parameters and that have a field messageType(inherited from Message). Then i create an instance of that class and check if the messageType is == to the first word in the user input (birthday in this case). If it is, then i create an instance of that class with the user provided parameters.
Is there a better way to do this with reflection?
If you want to go this route (I hate reflection, but it has it's uses) make sure you isolate it inside a factory class. I would recommend looking into #Annotations and mark the classes you want to be scanned with a specific annotation.
Something like: (must admit, actually had fun writing this example)
annotation:
#Target(value = {ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface GreetingcardInstance {
public String value();
}
Your 2 message classes and base class
public abstract class Message {
private String name;
public Message(String name) {
this.name = name; // not used, but to keep it in line with your example
}
}
#GreetingcardInstance("birthday")
public class BirthdayMessage extends Message {
public BirthdayMessage(Integer i) {
super("birthday");
// this line prints if this works.
System.out.println("Birthdaymessage created: " +i);
}
}
#GreetingcardInstance("other")
public class OtherMessage extends Message{
public OtherMessage(Integer i, Integer j) {
super("other");
}
}
And the factory that hides the nasty reflection code
public class CardFactory {
private final Map<String, Class> messageClasses;
public CardFactory() {
// I had all my implementations of Message in the package instances
Reflections reflections = new Reflections("instances");
Set<Class<?>> greetingCardAnnotations = reflections.getTypesAnnotatedWith(GreetingcardInstance.class);
Map<String, Class> result = new HashMap<String, Class>();
for (Class c : greetingCardAnnotations) {
if (Message.class.isAssignableFrom(c)) {
GreetingcardInstance annotation = (GreetingcardInstance) c.getAnnotation(GreetingcardInstance.class);
result.put(annotation.value(), c);
}
}
messageClasses = result;
}
public Message createMessage(String messageType, Object[] arguments) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class clazz = messageClasses.get(messageType);
if (clazz == null) {
throw new IllegalArgumentException("MessageType: " + messageType + " not supported");
}
Class[] argumentTypes = new Class[arguments.length];
for (int i = 0; i < arguments.length; ++i) {
argumentTypes[i] = arguments[i].getClass();
}
Constructor constructor = clazz.getConstructor(argumentTypes);
return (Message) constructor.newInstance(arguments);
}
}
You can either use spring or google's library or scan them by hand, though you'll find that's a pain in the behind. In this example I used the google library which works pretty well.
in this particular implementation the classes all live in the same package. I don't think this is too bad but might not work for you.
I've also not handled basic types, the constructors in this case take Integer, not int as I originally intended.
When you parse a string, just parse the arguments into String, INteger, etc. and pass them as an Object[] , whcih will be used as constructor args.
public static void main(String[] argv) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
CardFactory cf = new CardFactory();
System.out.println(cf.toString());
cf.createMessage("birthday", new Object[] { new Integer(0) });
}
output:
Birthdaymessage created: 0
Wouldn't it be simpler to instead of creating different classes for each message type use different formats which you could store somewhere like Map<String,String>?
I mean something like
Map<String,String> formats = new HashMap<>();
formats.put("birthday","Happy birthday %s. You are %d years old%n");//name, age
formats.put("farewell","%s %s%n");//message, name
Object[] data = {"Dany", 5};
System.out.printf(formats.get("birthday"),data);
data = new Object[]{"Ferwell Jack.","We will miss you"};
System.out.printf(formats.get("farewell"),data);
If you don't want to recompile your code after each change in formats you can store them in file and load when application starts, or when needed.
Simple way to do it is by java.util.Properties class.
You can create formats.properties file with content like
birthday=Happy birthday %s. You are %d years old%n
farewell=%s %s%n
and code using it could look like
Properties formatProp = new Properties();
formatProp.load(new FileReader("formats.properties"));//
Object[] data = {"Dany", 5};
System.out.printf(formatProp.getProperty("birthday"),data);
data = new Object[]{"Ferwell Jack.","We will miss you"};
System.out.printf(formatProp.getProperty("farewell"),data);
There are lots of ways to do what you want. One way would be to learn how to use an injection library like Google Guice. You'll probably get the most mileage out of that in the long run. Another option would be to learn a language like Clojure edit Clojure example added at end.
If you'd like to see a minimal example of what would look like Java, the following class' main will show you how to do that. Basically, it takes a map of String->Classnames (strings), and turns it into a map of String->Class (objects), then a super simple builder method looks up the codeword in the map and constructs a new instance that class and returns it.
The main builds two of them and prints their output. e.g.
I am a bar.
I'm a baz!
Here's the Java program. If you change the package, you'll have to change the classnames in the textConfig variable. The equivalent Clojure code follows.
package foo;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
public abstract class Foo {
public abstract String something(); // the abstract method
static class Bar extends Foo { // one subclass
#Override public String something() {
return "I am a bar.";
}
}
static class Baz extends Foo { // another subclass
#Override public String something() {
return "I'm a baz!";
}
}
public static Class loadClass(String classname) {
try { // wrapper for Class.forName that doesn't throw checked exception
return Class.forName(classname);
} catch (ClassNotFoundException ex) {
throw new IllegalArgumentException(ex);
}
}
public static Map<String, Class> buildConfig(Map<String, String> textConfig) {
// turn {codeword, classname} into {codeword, class} entries
// java 8 voodoo follows...
return textConfig.entrySet().stream().collect(Collectors.toMap(
Map.Entry::getKey,
e -> loadClass(e.getValue())));
}
public static Foo construct(Map<String, Class> config, String codeword) {
try { // lookup codeword and return new instance of class
return (Foo)config.get(codeword).newInstance();
}
catch(InstantiationException | IllegalAccessException ex) {
throw new IllegalArgumentException(ex);
}
}
public static void main(String[] args) {
// some configuration, you could hardcode this, or even put the
// data in annoations if you want to be fancy
Map<String, String> textConfig = new HashMap<>();
textConfig.put("codeword-bar", "foo.Foo$Bar");
textConfig.put("codeword-baz", "foo.Foo$Baz");
// turn your text config into something more immediately useful
Map<String, Class> config = buildConfig(textConfig);
// show that it works.
System.out.println(construct(config, "codeword-bar").something());
System.out.println(construct(config, "codeword-baz").something());
}
}
edit
The verbosity of the above code was driving me nuts. So, in case you're interested, here's equivalent code in Clojure.
It puts two functions into a map with the keys :bar and :baz and it looks them up, invokes them and prints the return value.
user=> (def config {:bar (fn [] "I am a bar.") :baz (fn [] "I'm a bar!")})
#'user/config
user=> (println ((:bar config)))
I am a bar.
nil
user=> (println ((:baz config)))
I'm a bar!
nil

How do I find the declared fields of a field?

So, I have this function:
public Object findObject(Object object, String name) {
if (object == null) {
return null;
}
for (Field foundField : object.getClass().getDeclaredFields()) {
if (foundField.getName().equalsIgnoreCase(name)) {
try {
return foundField.get(object);
} catch (IllegalArgumentException e) {
ClientImpl.getInstance().getUtil().addChatMessage(ClientImpl.getInstance().getLogo() + "Invalid argument.");
} catch (IllegalAccessException e) {
ClientImpl.getInstance().getUtil().addChatMessage(ClientImpl.getInstance().getLogo() + "Invalid argument.");
}
}
}
return null;
}
So that will return all the declared fields of an object and it's super class, I have this loop:
objectName = objectName.replace("main/", "");
String[] split = objectName.split("/");
for (int i = 0; i < split.length; i++) {
object = this.findObject(object, split[i]);
}
The inital object is a class, let's say that class is called Main.
I'd input something like this:
main/gameWorld/gameData
It would search the inital object for any fields called gameWorld, once found it would set the current object to gameWorld and search that object for any fields called gameData. This works, if I input something like this:
main/gameWorld
but if I do
main/gameWorld/gameData
It claims that object is null.
I think the problem might in that you're not querying fields in a superclass, so the field gameData is not found. From JavaDoc for getDeclaredFields:
Returns an array of Field objects reflecting all the fields declared by the class or interface represented by thisClass object. This includes public, protected, default (package) access, and private fields, but excludes inherited fields.
So if your GameWorld containing gameData is inherited from a superclass with this field, i.e.
class AbstractGameWorld{
GameData gameData;
}
class GameWorld extends AbstractGameWorld{
...
}
This would be the case.

Categories

Resources