Установка значения поля объекта, который является дочерним по отношению к другому объекту

#java #reflection

#java #отражение

Вопрос:

Я получаю следующее исключение:

java.язык.Исключение IllegalArgumentException: объект не является экземпляром класса

и у меня есть довольно хорошая идея о том, почему я это получаю, но я застрял в поиске решения.

Вот пример кода, объясняющий, что я хочу сделать:

 public class Car {
  private Owner owner;

  //Constructor and getter for owner field
}

public class Owner {
  private String name;

  //Constructor and getter for name field
}
  

Теперь то, что я пытаюсь сделать, это: начиная с класса Car, я извлекаю поле с именем «владелец» — затем я извлекаю поле «имя» этого владельца объекта, который является частью автомобиля. Теперь я хочу прочитать (и позже изменить, но у меня уже не получается с чтением) значение поля «name». Проблема: у меня есть только экземпляр car, но нет его поля «владелец» . В целях пояснения: описанный процесс должен работать в общем виде — поэтому я не буду знать имена получателей, установщиков, полей и так далее.

Вот код, который завершается с ошибкой (пожалуйста, дайте мне знать, если потребуется больше кода для прояснения проблемы):

 Car car = new Car();
//Set owner and name property of owner for car
...
Field nameField = resolveDatapath(datapathToFeature, car);
nameField.setAccessible(true);
//Here I fail with the afore mentioned IllegalArgumentException, because 
//indeed, I am passing car - not its sub-object "owner" (because as I can see it,
//there is no way for me to generically retrieve the owner object - or is there?)
String value = nameField.get(car).toString();
  

Вышеупомянутый материал также должен работать для любых «глубин» (скажем, я хочу поле и манипулирую им из объекта c, который является полем объекта b, который, в свою очередь, является полем объекта a …)

Пожалуйста, дайте мне знать, если я смогу дополнительно прояснить вопрос — вот код, который извлекает поле (в приведенном выше примере это поле nameField):

     private Field resolveDatapath(String path, Object parent) {
    String subString = path;
    if (!subString.contains("."))
    {
        //We haven reached the end of the path
        return getField(subString, parent.getClass());
    }       

    //We haven't reached the end of the 
    subString = path.substring(0, path.indexOf("."));   
    Field field = getField(subString, parent.getClass());

    return resolveDatapath(path.substring(path.indexOf(".") 1), field);
}

private Field getField(String name, Class<?> parent) {
    Field [] fields = parent.getDeclaredFields();

    for(Field f : fields)
    {           
        String current = f.getName();
        if(current.equalsIgnoreCase(name))
        {
            try {
                return parent.getDeclaredField(current);
            } catch (SecurityException e) {
                fail("Not allowed to access field - "   current);
            } catch (NoSuchFieldException e) {
                fail("No such field exists - "   current);
            }
        }
    }
    return null;
}
  

Заранее большое вам спасибо,
с наилучшими пожеланиями,
Ready4Android

Ответ №1:

Поле предоставит вам только поля для данного класса. Он не будет переходить в класс и получать подполя.

Я предлагаю вместо этого использовать простой помощник get field, подобный этому

   String name = (String) getField(car, datapathToFeature);
  

Комментарии:

1. Спасибо за предложение — но я не уверен, что полностью понимаю ваш ответ 🙂 … Что у меня есть, так это путь к нужному мне полю, например: Автомобиль. Владелец. Name (поле name в классе owner, которое является полем в классе car) — моя проблема заключается в том, чтобы установить и получить значения этого поля name, потому что у меня нет родительского объекта в качестве экземпляра (я могу получить его только как поле)… привет … лол, пока я пишу это, у меня появилась еще одна идея… Я вернусь с обновлениями 🙂

Ответ №2:

Ладно, спасибо Питеру за то, что он дал моему мозгу толчок, в котором он нуждался 😉 Я нашел решение, и вот оно:

Я создаю DatapathObject, который содержит поле и родительский объект в качестве объекта. До того, как я написал, что я не могу получить экземпляр объекта Owner, который является полем в объекте Car. Конечно , это полная чушь , как я теперь понял — мой мозг работал здесь довольно медленно : P .

Этот код работает и делает мой самый простой проход:

     public void testDatapathResolution() throws Exception{
    String[] path = datapathToFeature.split("\.");
    assertEquals("Owner", path[0]);
    assertEquals("Name", path[1]);
    DatapathObject result = resolveDatapath(datapathToFeature, car);
    result.getField().setAccessible(true);
    Object value = result.getField().get(result.getParent());

    assertEquals(car.getOwner().getName(), value.toString());
}
  

А вот и класс DatapathObject:

 public class DatapathObject {
  private Object parent;
  private Field field;

  public DatapathObject(Object parent, Field field) {
    this.parent = parent;
    this.field = field;
  }

  public Object getParent() {
    return parent;
  }

  public Field getField() {
    return field;
  } 
}
  

Мой метод resolveDatapath выполнен следующим образом (для размещения введения DatapathObject:

     private DatapathObject resolveDatapath(String path, Object parent) throws 
    IllegalArgumentException, IllegalAccessException
{
    String subString = path;
    if (!subString.contains("."))
    {
        //We haven reached the end of the path
        Field field = getField(subString, parent.getClass());
        return new DatapathObject(parent, field);
    }       

    //We haven't reached the end of the 
    subString = path.substring(0, path.indexOf("."));   
    Field field = getField(subString, parent.getClass());
    field.setAccessible(true);
    return resolveDatapath(path.substring(path.indexOf(".") 1),  
            field.get(parent));
}
  

Еще раз спасибо Peter — lol Я все еще не понимаю вашего ответа, и я не думаю, что это было то, что я искал, но это дало моему мозгу недостающую искру, чтобы найти ответ 🙂 !