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 ответов:
Вы можете сделать это с помощью дженериков и служебного класса, чтобы сделать эту работу. Мне это нравится немного больше, чем использование отражения, потому что вы получаете время компиляции, проверяя, что свойство, которое вы получаете, существует (и не ошибается и т. д.).
Первый класс, который выполняет фактическое преобразование. Обратите внимание на абстрактное "вам" метод, который мы будем переопределять в анонимный внутренний класс, когда мы называем 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; }