-1

I have a class (it's a Singleton in my project) with some variables. I would like to get one of those variable by passing its name in a function. For now i have :

public final class Configuration {
   private static volatile Configuration instance = null;
   //Here usual code to instanciate singleton

   String var1;
   int var2;
   boolean var3;

   public static Object Get(String varname)throws IllegalArgumentException, IllegalAccessException {
       for(Field field : instance.getClass().getFields())
            if(field.getName() == varname) 
                return field.get(field);
       return null;
   }

}

From the debugger i know that instance.getClass().getFields() return an empty Field[], but i do not understand why.

Foxhunt
  • 764
  • 1
  • 9
  • 23

2 Answers2

3

You probably don't want to do this. But okay,

You compare strings and other well defined objects like this:

boolean b = java.lang.Objects.equals(a, b)

Additionally getFields provides only public fields. See What is the difference between getFields and getDeclaredFields in Java reflection

Aside: try not to return null. Prefer java.lang.Optional<T> or throw an exception

Keynan
  • 1,338
  • 1
  • 10
  • 18
  • Ok i suppose i need to use '''Class.getDeclaredFields()''' instead. I am knew in Java i though the '''==''' operator did the job well. And I am actually catching null as an answer in others parts though, but yes i undersand throwing exception is better. – Foxhunt Jan 27 '20 at 00:58
  • ```java.lang.Objects.equals(a, b)``` is actually ```a.equals(b)``` – Foxhunt Jan 27 '20 at 01:18
  • @Phoenix is correct that under the hood it is a.equals(b) with 2 caveats. `Objects.equals(a, b)` will work for primitives (by auto-boxing i.e. int becomes Integer), and `Objects.equals(a, b)` works correctly if a is null, where as `a.equals(b)` throws a null pointer exception. The latter is why `Objects.equals(a, b)` is preferred. `x == y` works (and is preferred for primitives (int / float / double) but for objects it compares pointer values (references) so it will only return true if they are the same object in memory. – Keynan Jan 27 '20 at 04:26
  • 1
    Since `field.getName()` never returns `null`, `field.getName().equals(varname)` would do. I would ever prefer `varname.equals(field.getName())`, as it is better to flag a `null` input with an exception, instead of iterating over all fields and eventually return `null` (or an empty optional) without a hint that this is not a “no match” scenario but a programming error. And even less recommended is using `Objects.equals(…)` for primitive types. For primitive types `0 == 0.0` and `'A' == 65` whereas `Objects.equals(0,0.0)` and `Objects.equals('A', 65)` will each evaluate to `false`. – Holger Jan 28 '20 at 16:15
  • At the risk of getting philosophical `'A' == 65` SHOULD evaluate to false. These things are not the same type. The fact that `'A' == 65` will work opens the language up to performing hacky calculations. That might be a good thing in special circumstances but should not be normal behaviour. If java had a better type system `Objects.equals('A', 65)` wouldn't even compile – Keynan Jan 28 '20 at 19:07
  • 1
    At the risk of getting philosophical, a method that literally reads as “objects equals” is not the right thing for primitive types. What about `0 == 0.0` or `0 == 0L` or `-0.0 == +0.0` (even same type on both sides) or `Double.NaN == Double.NaN`? I only used a `char` literal, because it intrinsically maps to a different integer type without the need for a cast. Semantically, nothing stops you from comparing an `int` representing a codepoint with an `int` representing, e.g. an x screen coordinate. It doesn’t help considering an UTF-16 unit as special, just because it has a legacy built-in type. – Holger Jan 30 '20 at 11:54
1

Besides your error with the String compare (You should be using .equals, not ==, even though == will often work.) you also have a problem with expression field.get(field). This would get a value from the field object, which won't have a field named varname. You probably want field.get(instance). Even with this corrected, I'm not entirely sure what it will do with the primitive values -- the int and the boolean -- but I imagine it will convert them to Integer and Boolean respectively, if it does anything.

But let's back up even further. I strongly suspect that this approach is not the answer to your problem. If you plan to access these values by name, then they should be in a Map, in which case you don't need reflection to get at them. If you really want to access the data with normal methods as well as by name, you can create accessor methods which then get/put the data to/from the map.

Zag
  • 638
  • 4
  • 8
  • I could set individual accessors with a big ```switch```, but with a great amount of var it is easier to do that. And my purpose is also to learn new patterns. – Foxhunt Jan 27 '20 at 01:02