Java предотвращает дублирование кода при получении списка свойств в каждом объекте из списка объектов


У меня есть несколько объектов, которые выглядят так:

class PhoneNumber
{
   String   getNumber();
   String   getExtension();
   DateTime getLastCalled();
}
class Address
{
   String getCity();
   string getState();
   int    getZip();
}
Я хотел бы иметь возможность взять список любого из этих объектов и получить список конкретного свойства. Это тривиальная операция, если бы я написал функцию для каждого свойства, но, как я понимаю, это плохая практика, чтобы дублировать код так много раз.

Поэтому я хотел бы иметь возможность сделать что-то подобное для любого свойства в любом объекте:

List<Address> AddressList = getAddresses();
List<String> CityList = getListOfProperties( AddressList, Address.getCity /*<--Instruction as to what property to populate the returned list with */ )

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

5 3

5 ответов:

Вы можете сделать это с помощью дженериков и служебного класса, чтобы сделать эту работу. Мне это нравится немного больше, чем использование отражения, потому что вы получаете время компиляции, проверяя, что свойство, которое вы получаете, существует (и не ошибается и т. д.).

Первый класс, который выполняет фактическое преобразование. Обратите внимание на абстрактное "вам" метод, который мы будем переопределять в анонимный внутренний класс, когда мы называем getListOfProperties().

public abstract class ListPropertyGetter<R,T>
{
  /** Given a source object, returns some property of type R. */ 
  public abstract R get(T source);

  /** Given a list of objects, use the "get" method to retrieve a single property
      from every list item and return a List of those property values. */
  public final List<R> getListOfProperties(List<T> list)
  {
    List<R> ret = new ArrayList<R>(list.size());
    for(T source : list)
    {
      ret.add(get(source));
    }
    return ret;
  }
}

Тогда вы используете его вот так. Не очень симпатичный лайнер, но время компиляции проверка благости:

List<PhoneNumber> phoneNumbers = new ArrayList<PhoneNumber>();
// add some PhoneNumbers to the list
List<String> extensions =
  new ListPropertyGetter<String, PhoneNumber>()
  {
    @Override
    public String get(PhoneNumber source)
    {
      return source.getExtension();
    }
  }.getListOfProperties(phoneNumbers);

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

Вы могли бы обращаться с вашими объектами с помощью Introspector как в это так ответьте .

public <T > List<T > getMemberList( List<? > beanList, 
        String propertyName, Class<T > resultClass ) throws Exception {
    List<T > result = new ArrayList<T >();
    for( Object bean : beanList ) {
        result.add( (T )new PropertyDescriptor( 
                propertyName, bean.getClass() ).getReadMethod().invoke( bean ) );
    }
    return result;
}

Тогда вы можете сделать такой вызов:

List<String > result = getMemberList( phonenumberList, "number", String.class );

Вы можете использовать reflection (java.яз..Класс и java.яз..отражать.* ), чтобы получить имена методов; те, которые начинаются с "get", являются свойствами, имя которых следует за "get".

Это довольно хороший случай для замыканий в Java; он приближается,но его еще нет.

Тем временем, вы можете сделать это с помощью рефлексии, хотя рефлексия всегда имеет штраф за производительность. Одна из вещей, которые может сделать отражение, - это позволить вам вызвать метод для объекта или получить свойство, указав имя метода / свойства в виде строки; в вашем случае вы хотели бы сделать getListOfProperties(AddressList, "city") и в этом методе использовать отражение для получения указанного свойства.

Это тип вещи значительно упрощается с помощью библиотекиApache Commons BeanUtils .

import org.apache.commons.beanutils.BeanUtils;

static List<String> getListOfProperties(List beans, String propertyName) {
    List<String> propertyValues = new ArrayList<String>();
    for (Object bean:beans) {
         propertyValues.add(BeanUtils.getProperty(bean, propertyName));
    }
    return propertyValues;
}