Java Предотвращение дублирования кода при получении списка свойств каждого объекта из списка объектов - PullRequest
3 голосов
/ 03 февраля 2010

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

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 ]

2 голосов
/ 04 февраля 2010

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

Сначала класс, который выполняет фактическое преобразование. Обратите внимание на абстрактный метод «get», который мы переопределим в анонимном внутреннем классе, когда вызовем 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);
2 голосов
/ 03 февраля 2010

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

2 голосов
/ 03 февраля 2010

Вы можете обрабатывать свои объекты с помощью Introspector, как в этом SO-ответе .

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 );
1 голос
/ 03 февраля 2010

Это довольно хороший случай для замыканий в 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;
}
1 голос
/ 03 февраля 2010

Вы можете использовать отражение (java.lang.Class и java.lang.reflect. *), Чтобы получить имена методов; те, которые начинаются с «get» - это свойства, имя которых следует за «get».

...