Получение поля Java с помощью отражения, но не из его строкового имени - PullRequest
1 голос
/ 22 марта 2011

Можно ли получить поле с помощью отражения Java, если у меня есть само поле? Это примитивный поплавок (публично, без проблем). Я не хочу использовать его имя в качестве строки.

Пример:

public class TVset {
  public float voltageA;
  public float voltageB;
  public float voltageC;
  public TVset(...) {...} // constructor
  public void function() {...} // it changes voltages
}

class Voltmeter{
  Object theObject;
  Field theField;

  Voltmeter(Object obj) {
    theObject = obj;
    Class theFieldClass = obj.getClass();
    Class theContainerClass = theFieldClass.getDeclaringClass();
    Field theField = ??? // <-- here I don't want to use a String
  }

  float getVoltage() {
    return theField.getFloat(theObject);
  }
}

TVset tv1 = new TVset(...);
TVset tv2 = new TVset(...);

Voltmeter meter = new Voltmeter(tv1.voltageB);
meter.getVoltage();
tv1.function();
meter.getVoltage(); <- should reflect the changed voltage
tv1.function();
meter.getVoltage(); <- should reflect the changed voltage
...

Эффект аналогичен передаче поплавка по ссылке, но без переноса его в класс-оболочку.

Мне нужно измерить разные напряжения на разных телевизорах, просто изменив линию:

Voltmeter meter = new Voltmeter(tv1.voltageB);

к чему-то другому, например:

Voltmeter meter = new Voltmeter(tv2.voltageA);

Можно ли сделать это с помощью отражения?

Thx

Ответы [ 4 ]

2 голосов
/ 22 марта 2011

Просто для полноты я включил делегатский способ решения этой проблемы. Я также не рекомендовал бы иметь ваши поплавки с публичным доступом.

public class stackoverflow_5383947 {

    public static class Tvset {

        public float voltageA;
        public float voltageB;
        public float voltageC;

        public Tvset() {
        }

        public void function() {
            voltageA++;
        }
    };

    public static class Voltmeter {

        private VoltageDelegate _delegate;

        public Voltmeter(VoltageDelegate delegate) {
            _delegate = delegate;
        }

        float getVoltage() {
            return _delegate.getVoltage();
        }
    };

    public static interface VoltageDelegate {

        public float getVoltage();
    }   

    public static void main(String[] args) {
        final Tvset tv1 = new Tvset();
        Voltmeter meter = new Voltmeter(new VoltageDelegate()   {
            public float getVoltage() {
                return tv1.voltageA;
            }
        });

        System.out.println(meter.getVoltage());
        tv1.function();
        System.out.println(meter.getVoltage());
        tv1.function();
        System.out.println(meter.getVoltage());
    }
}
2 голосов
/ 22 марта 2011

Чтобы использовать отражение, вы должны использовать строку.Вместо использования float вы можете использовать объект для переноса изменяемого float или простого float [1];

Кстати, я бы не использовал float, если у вас нет действительно веских причин, double страдает гораздо меньшей ошибкой округления.

public class TVset {
  public double[] voltageA = { 0.0 };
  public double[] voltageB = { 0.0 };
  public double[] voltageC = { 0.0 };
}

class Voltmeter{
  final double[] theField;

  Voltmeter(double[] theField) {
    this.theField = theField;
  }

  double getVoltage() {
    return theField[0];
  }
}
// works just fine.
Voltmeter meter = new Voltmeter(tv1.voltageB);

РЕДАКТИРОВАТЬ: Использование абстрактного метода доступа.Это самый быстрый способ сделать это.AFAIK, разница составляет менее 10 наносекунд.

public abstract class Voltmeter{ // or use an interface
  public abstract double get();
  public abstract void set(double voltage);
}

public class TVset {
  private double _voltageA = 0.0;
  private double _voltageB = 0.0;
  private double _voltageC = 0.0;
  public final Voltmeter voltageA = new Voltmeter() {
     public double get() { return _voltageA; }
     public void set(double voltage) { _voltageA = voltage; }
  }
  public final Voltmeter voltageB = new Voltmeter() {
     public double get() { return _voltageB; }
     public void set(double voltage) { _voltageB = voltage; }
  }
  public final Voltmeter voltageC = new Voltmeter() {
     public double get() { return _voltageC; }
     public void set(double voltage) { _voltageC = voltage; }
  }
}

Лично, если скорость критична, я бы просто использовал поля непосредственно по имени.Вы не станете проще или быстрее, чем это.

0 голосов
/ 22 марта 2011

Вот вариант, в котором вы можете указать значение float вместо строки.

class Voltmeter{
  Object container;
  Field theField;

  Voltmeter(Object obj, float currentValue) {
    container = obj;
    Class<?> containerClass = obj.getClass();
    Field[] fields = containerClass.getFields();
    for(Field f : fields) {
       if (f.getType() == float.class &&
           f.getFloat(container) == currentValue) {
          this.theField = f;
          break;
       }
    }
  }

  float getVoltage() {
    return theField.getFloat(container);
  }
}

Затем вызвать его так:

Voltmeter meter = new Voltmeter(tv1, tv1.voltageB);

Это работает, только если напряжениев момент создания вольтметра они различны (а не NaN), так как для него берется первое поле с нужным значением.И это не совсем эффективнее, я думаю.

Я бы не стал этого рекомендовать.

0 голосов
/ 22 марта 2011

Если вы управляете TVSet, но по какой-то причине вам нужно использовать отражение, хороший способ избежать ошибок - записать имена методов / полей, которые вам нужны как строковые константы в классе TVSet.

Однако еслиВас беспокоит производительность, рефлексия - это не тот путь, по которому доступ к полю или методу с помощью рефлексии может быть намного медленнее, чем доступ через геттеры или напрямую.

...