在Java中通过名称设置变量

17

我希望在Java中实现类似以下的功能:

class Foo{
 private int lorem; //
 private int ipsum;      

 public setAttribute(String attr, int val){
  //sets attribute based on name
 }

 public static void main(String [] args){
  Foo f = new Foo();
  f.setAttribute("lorem",1);
  f.setAttribute("ipsum",2);
 }

 public Foo(){}
}

是否有可能根据变量名称设置变量,而不硬编码变量名称并且不使用任何其他数据结构?

6个回答

36

这是使用反射实现setAttribute的示例代码(我已将函数重命名;不同的字段类型需要使用不同的反射函数):

public void setIntField(String fieldName, int value)
        throws NoSuchFieldException, IllegalAccessException {
    Field field = getClass().getDeclaredField(fieldName);
    field.setInt(this, value);
}

5

一般情况下,您需要使用反射。这里是一个很好的介绍 主题及其示例

特别是,“更改字段的值”部分描述了如何实现您想要的功能。

我注意到作者说:“这个特性非常强大,在其他传统语言中没有相当之处。” 当然,在过去的十年中(文章写于1998年),我们见证了动态语言方面的巨大进步。在Perl、Python、PHP、Ruby等语言中,以上操作都比较容易实现。我猜测您可能已经从“eval”标签中得到了启示。


链接似乎已经失效了 ;( - Vlad
1
重新修复了链接,使其指向archive.org对该资源的捕获。 - Mat Kelly

4
此外,还要看一下BeanUtils,它可以为您隐藏使用反射时的一些复杂性。

3
这个问题具体针对整数,这很有帮助。但是这里有一些更通用的内容。如果你正在加载字段名/字段值对的字符串表示形式,则此类型的方法非常有用。
import java.lang.reflect.Field;

public class FieldTest {

    static boolean isValid = false;
    static int count = 5;

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        FieldTest test = new FieldTest();
        test.setProperty("count", "24");
        System.out.println(count);
        test.setProperty("isValid", "true");
        System.out.println(isValid);
    }

    public void setProperty(String fieldName, String value) throws NoSuchFieldException, IllegalAccessException {
        Field field = this.getClass().getDeclaredField(fieldName);
        if (field.getType() == Character.TYPE) {field.set(getClass(), value.charAt(0)); return;}
        if (field.getType() == Short.TYPE) {field.set(getClass(), Short.parseShort(value)); return;}
        if (field.getType() == Integer.TYPE) {field.set(getClass(), Integer.parseInt(value)); return;}
        if (field.getType() == Long.TYPE) {field.set(getClass(), Long.parseLong(value)); return;}
        if (field.getType() == Float.TYPE) {field.set(getClass(), Float.parseFloat(value)); return;}
        if (field.getType() == Double.TYPE) {field.set(getClass(), Double.parseDouble(value)); return;}
        if (field.getType() == Byte.TYPE) {field.set(getClass(), Byte.parseByte(value)); return;}
        if (field.getType() == Boolean.TYPE) {field.set(getClass(), Boolean.parseBoolean(value)); return;}
        field.set(getClass(), value);
    }

}

1
根据使用情况,您可以像上面建议的那样使用反射,或者也许HashMap更适合...

-1

在此过程中,您可能希望缓存一些反射数据:

import java.lang.reflect.Field;
import java.util.HashMap;

class Foo {
    private HashMap<String, Field> fields = new HashMap<String, Field>();

    private void setAttribute(Field field, Object value) {
        field.set(this, value);
    }

    public void setAttribute(String fieldName, Object value) {
        if (!fields.containsKey(fieldName)) {
            fields.put(fieldName, value);
        }
        setAttribute(fields.get(fieldName), value);
    }
}

为什么要费心,反射层已经做了这个缓存?阅读java.lang.Class的源代码并自行查看;它甚至在类重新加载时处理无效缓存。 - C. K. Young

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接