21

Example code:

int width = 5;
int area = 8;
int potato = 2;
int stackOverflow = -4;

Now, say I want to have the user input a string:

String input = new Scanner(System.in).nextLine();

Then, say the user inputs potato. How would I retrieve the variable named potato and do stuff with it? Something like this:

System.getVariable(input); //which will be 2
System.getVariable("stackOverflow"); //should be -4

I looked up some things and did not find much; I did find a reference to something called "the Reflection API," but that seems too complicated for this one simple task.

Is there a way to do this, and if so, what is it? If "Reflection" does indeed work and if it is the only way, then how would I use it to do this? The tutorial page for it has all sorts of internal stuff that I can't make any sense of.

EDIT: I need to keep the Strings in the variables for what I am doing. (I can't use a Map)

tckmn
  • 57,719
  • 27
  • 114
  • 156

6 Answers6

17

Using reflection doesn't seem like a good design for what you're doing here. It would be better to use a Map<String, Integer> for example:

static final Map<String, Integer> VALUES_BY_NAME;
static {
    final Map<String, Integer> valuesByName = new HashMap<>();
    valuesByName.put("width", 5);
    valuesByName.put("potato", 2);
    VALUES_BY_NAME = Collections.unmodifiableMap(valuesByName);
}

Or with Guava:

static final ImmutableMap<String, Integer> VALUES_BY_NAME = ImmutableMap.of(
    "width", 5,
    "potato", 2
);

Or with an enum:

enum NameValuePair {

    WIDTH("width", 5),
    POTATO("potato", 2);

    private final String name;
    private final int value;

    private NameValuePair(final String name, final int value) {
        this.name = name;
        this.value = value;
    }

    public String getName() {
        return name;
    }

    public String getValue() {
        return value;
    }

    static NameValuePair getByName(final String name) {
        for (final NameValuePair nvp : values()) {
            if (nvp.getName().equals(name)) {
                return nvp;
            }
        }
        throw new IllegalArgumentException("Invalid name: " + name);
    }
}
Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
4

Variable names are only available at compiler time. Reflection only gives access to class declarations and items declared inside them, but not to local variables. I suspect that a Map of some kind will be a more appropriate solution to your real problem. Specifically, check out HashMap and TreeMap.

Code-Apprentice
  • 81,660
  • 23
  • 145
  • 268
3

Instead of trying to find the value of a variable name, why don't you use a Map with a key/value pair?

Map<String, Integer> vars = new HashMap<String, Integer>();
vars.put("width",5);
vars.put("area",8);
vars.put("potato", 2);
vars.put("stackOverflow",-4);

Then you could access the inputs like so:

vars.get(input); //would be 2
vars.get("stackOverflow"); //would be -4
alexander
  • 1,191
  • 2
  • 20
  • 40
mbumiller
  • 114
  • 2
  • 6
3

I have a solution for this problem that does not involve using a map. I ran into this technique because we had several variables that needed to be update based on something within the variable name itself. However, the best way to do this is by using the getters/setters rather than the variables.

After you create your class, you can access the methods by creating Method objects and invoking them individually.

public class FooClass

private String foo1;
private String foo2;

public String getFoo1();
public String getFoo2();

FooClass fooClass = new FooClass();

Method mFoo1 = fooClass.getClass().getMethod("getFoo" + increment + "()");

mFoo1 .invoke(fooClass);

However, this would not be limited to only incremental numbers, as long as you can get the string to match the method exactly.

String value = "Potato";
Method mPotato = myClass.getClass().getMethod("get" + value+ "()");

mPotato.invoke(myClass);
David Passmore
  • 6,089
  • 4
  • 46
  • 70
  • Thanks for the answer. My findings show that you don't need the parenthesis when looking up the method. Also you should use `getDeclaredMethod()` if you're looking for `private` methods - `getMethod()` only finds public methods. – David Passmore Nov 08 '19 at 15:45
2

I have another solution without a map :

class Vars {
    Integer potato, stack;
    public Vars(a,b) {
        potato=a;
        stack=b;
    }
}
Object object=(Object)new Vars(1,2);
Class<?> c = object.getClass();
Integer result=(Integer)c.getField("potato").get(object);
Luatic
  • 8,513
  • 2
  • 13
  • 34
0

Very redundant, but you can keep your variable names when using a map:

int width = 5;
int area = 8;
int potato = 2;
int stackOverflow = -4;
Map<String, Integer> map = new HashMap<>();
map.put("width", width);
map.put("area", area);
map.put("potato", potato);
map.put("stackOverflow", stackOverflow);

But a statement like this:

width = 42;

would not change the value in the Map:

String input = "width";
map.get(input); // <-- still returns 5.

Only a new call of put fixes that:

width = 42;
map.put("width", width);
// or
map.put("width", 42);
jlordo
  • 37,490
  • 6
  • 58
  • 83