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.
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.
So the issue I'm running into is this. I've written a method as a part of a program that I'm using to display all the objects in an ArrayList. There are three different types of objects stored in this ArrayList: Fungus, Flowers and Weeds. I'm able to call plantList.get(i).getName() and plantList.get(i).getColor methods with no issue. Both of these variables belong to the parent class called Plant. However, when calling the following method plantList.get(i).getPoison() (this method belongs to the subclass Fungus) I get a compiler error saying that it cannot find the variable Fungus.
I've tried it with every other variable unique to a subclass, and the same things happens. So I can access variables from the parent class 'Plant' but not from any of the subclasses of 'Fungus' 'Flower' or 'Weed'. I'm new to using subclasses and superclasses so I'm having a hard time figuring out exactly where the issue is arising.
public static void displayPlant(ArrayList<Plant> plantList) {
for (int i = 0; i < plantList.size(); i++) {
System.out.print(plantList.get(i).getName());
System.out.print(plantList.get(i).getID());
System.out.print(plantList.get(i).getColor());
if (plantList.get(i).contains(Fungus) == true) {
System.out.print(plantList.get(i).getPoison());
}
else if (plantList.get(i).contains(Flower) == true) {
System.out.print(plantList.get(i).getSmell());
System.out.print(plantList.get(i).getThorns());
}
else {
System.out.print(plantList.get(i).getPoison());
System.out.print(plantList.get(i).getEdible());
System.out.print(plantList.get(i).getMedicinal());
}
}
}
A good solution is to use dynamic dispatch. I.e. let the element itself decide what information it wants to print.
class Plant {
...
public String toString() {
return String.join(" ", getName(), getID(), getColor());
}
}
class Fungus extends Plant {
...
#Override
public String toString() {
return String.join(" ", super.toString(), getPoison());
}
}
class Flower extends Plant {
...
#Override
public String toString() {
return String.join(" ", super.toString(), getSmell(), getThorns());
}
}
class Weed extends Plant {
...
#Override
public String toString() {
return String.join(" ", super.toString(), getPoison(), getEdible(), getMedicinal());
}
}
Your loop will look like this:
public static void displayPlant(ArrayList<Plant> plantList) {
for(Plant p : plantList)
System.out.println(p); // This calls toString
}
Actually you are doing it in a wrong way,
Once you keep the object of child class into parent class variable then at compile time it search or find only the methods and variables that are declared in the parent class.
if you want to access the child class variables then you need to first find out the child class and then typecast it according
public static void displayPlant(ArrayList<Plant> plantList) {
for (int i = 0; i < plantList.size(); i++) {
Plant plant=plantList.get(i);
if (plant instanceof Flowers) {
Flowers fl=(Flowers)plant;
//TODO do whatever you want to do
}
else if(plant instanceof Weeds) {
Weeds wd=(Weeds)plant;
//TODO do whatever you want to do
}else if (plant instanceof Fungus) {
Fungus wd=(Fungus)plant;
//TODO do whatever you want to do
}
}
I hope it help you out. Thanks
Java is strongly typed language. That means that, if you want to use methods of child class you have to cast down to child class.
In your example:
...
else if (plantList.get(i) instanceof Fungus) { //check if plant is fungus
System.out.print(plantList.get(i).getSmell());
System.out.print(((Fungus)plantList.get(i)).getThorns()); //downcasting
}
...
Your check using constains wouldn't work. To check type of object you need to use operator instanceof.
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).
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.
How to check if given class has specific field and if it is initialized (has value at the moment)?
abstract class Player extends GameCahracter {
}
public class Monster extends GameCahracter{
public int level = 1;
}
abstract class GameCharacter{
public void attack(GameCahracter opponent){
if (opponent instanceof Monster && ){ // << here I have to know is it instance of Monster and if it has initialized value
}
}
To see if a class has a property without rely on exception, you can use these methods:
private Boolean objectHasProperty(Object obj, String propertyName){
List<Field> properties = getAllFields(obj);
for(Field field : properties){
if(field.getName().equalsIgnoreCase(propertyName)){
return true;
}
}
return false;
}
private static List<Field> getAllFields(Object obj){
List<Field> fields = new ArrayList<Field>();
getAllFieldsRecursive(fields, obj.getClass());
return fields;
}
private static List<Field> getAllFieldsRecursive(List<Field> fields, Class<?> type) {
for (Field field: type.getDeclaredFields()) {
fields.add(field);
}
if (type.getSuperclass() != null) {
fields = getAllFieldsRecursive(fields, type.getSuperclass());
}
return fields;
}
And simply call:
objectHasProperty(objInstance, "myPropertyName");
In fact does not matter the instance of the class to see if the class has the property, but I made that way, just to be little more friendly.
Just to conclude: I made the getAllFields to be recursive, to get all the superclasses methods too (in my case this is important)
After that, if you want to see what is the value of the property in the desired object, you can just call:
PropertyUtils.getProperty(objInstance, "myPropertyName");
Remember: if objInstance does not have that property, the call above will throw NoSuchMethodException (That is why you need to use the fist code to see if the class has the property)
You can use reflection, for example like this:
Class.forName("Foo").getFields()
And then you can check again if particular object has this field initialiazed by using reflection.
You do not have to use reflection for this you can simply do it with if condition.
if (opponent !=null && opponent instanceof Monster && ((Monster) opponent).level==1){ // << here I have to know is it instance of Monster and if it has initialized value
}
You can check the instance is not null if instance not null and its an instance of Monster then in your case its definitely initialized. Instance Variables initialized with default values whenever a new instance created if the opponent instance of a monster then level has value 1.
In addition to the answer of #Renato Lochetti i want to add a variant with only a single function, not using 3:
private boolean hasField(final Class<?> clazz, final String fieldName) {
// gather all fields of this class
final List<Field> fields = new ArrayList<>();
Class<?> current = clazz;
do {
Collections.addAll(fields, current.getDeclaredFields());
} while ((current = clazz.getSuperclass()) != null);
// check if field exists
return fields.stream().map(Field::getName).anyMatch(f -> f.equals(fieldName));
}
//Load the class
Class clazz = Class.forName("your.class.ClassName");
Field field = clazz.getField("fieldName")
if(field!=null){
//field exist now check if its initialized or not, or if its primitive field check against its assumed initialized value
if(ClassName.fieldName!=null){
//yes initilized
}
}