web-dev-qa-db-ja.com

文字列から名前で変数を取得する

コード例:

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

ここで、ユーザーに文字列を入力させたいとします。

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

次に、ユーザーがpotatoと入力するとします。 potatoという名前の変数をどのように取得し、それを処理しますかこのようなもの:

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

調べてみたがあまり見つからなかった。 「リフレクションAPI」と呼ばれるものへの参照を見つけましたが、これはこの1つの単純なタスクには複雑すぎるようです。

これを行う方法はありますか?ある場合、それは何ですか? 「リフレクション」が実際に機能し、それが唯一の方法である場合、それをどのように使用してこれを行うのですか?そのためのチュートリアルページには、私には理解できないあらゆる種類の内部的なものが含まれています。

編集:Stringsを変数に保持する必要があります。 (Mapは使用できません)

16
Doorknob

リフレクションを使用することは、ここでの作業に適したデザインのようには見えません。たとえば、Map<String, Integer>を使用することをお勧めします。

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);
}

または Guava を使用:

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

または 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);
    }
}
16
Paul Bellora

変数名の値を見つける代わりに、キーと値のペアを持つマップを使用してみませんか?

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

次に、次のように入力にアクセスできます。

vars.get(input); //would be 2
vars.get("stackOverflow"); //would be -4
3
mbumiller

変数名はコンパイラー時にのみ使用できます。リフレクションは、クラス宣言とその内部で宣言された項目へのアクセスのみを提供し、ローカル変数へのアクセスは提供しません。ある種のMapが実際の問題に対するより適切な解決策になると思います。具体的には、HashMapTreeMapを確認してください。

2
Code-Apprentice

マップの使用を含まないこの問題の解決策があります。変数名自体の中の何かに基づいて更新する必要があるいくつかの変数があったため、この手法に遭遇しました。ただし、これを行う最善の方法は、変数ではなくゲッター/セッターを使用することです。

クラスを作成したら、Methodオブジェクトを作成して個別に呼び出すことで、メソッドにアクセスできます。

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);

ただし、文字列をメソッドに正確に一致させることができる限り、これは増分数のみに限定されません。

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

mPotato.invoke(myClass);
2

地図なしの別の解決策があります:

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);
1
LMD

非常に冗長ですが、マップを使用するときに変数名を保持できます。

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);

しかし、このような声明:

width = 42;

マップの値は変更されません。

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

Put fixの新しい呼び出しのみ:

width = 42;
map.put("width", width);
// or
map.put("width", 42);
0
jlordo