【问题标题】:Java Prevent code duplication when getting a list of a property in each Object from a list of ObjectsJava从对象列表中获取每个对象中的属性列表时防止代码重复
【发布时间】:2010-02-03 20:10:28
【问题描述】:

我有几个看起来像这样的对象:

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 编程语言可以完成很多神奇的事情。

【问题讨论】:

    标签: java generics code-duplication


    【解决方案1】:

    您可以使用泛型和实用程序类来完成这项工作。我喜欢这比使用反射好一点,因为您可以在编译时检查您正在检索的属性是否存在(并且没有拼写错误等)。

    首先是一个进行实际转换的类。注意当我们调用 getListOfProperties() 时,我们将在匿名内部类中重写的抽象“get”方法。

    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);
    

    【讨论】:

    • 伙计,在我放弃并发布这个问题之前,我已经非常接近类似的东西了。谢谢一百万!
    • 您的ListPropertyGetter 类足够通用,可以对输入项执行任何操作以生成输出项,而不仅仅是获取属性。这通常称为map(在Linq 中除外,它称为Select)。
    【解决方案2】:

    对于这么简单的事情,最好只提供 getter 和 setter。拒绝复制一行代码并没有节省太多。更好的是,只需让您的 IDE 为您编写它。

    【讨论】:

    • 一旦你提供访问器方法,你就是在邀请重复代码。
    【解决方案3】:

    您可以使用Introspector 处理您的对象,例如this SO answer

    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 );
    

    【讨论】:

      【解决方案4】:

      您可以使用反射(java.lang.Class 和 java.lang.reflect.*)来获取方法名称;以“get”开头的是属性,其名称跟在“get”之后。

      【讨论】:

        【解决方案5】:

        这对于 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;
        }
        

        【讨论】:

          猜你喜欢
          • 2021-10-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-09-03
          • 1970-01-01
          • 2017-06-29
          • 2012-11-11
          • 1970-01-01
          相关资源
          最近更新 更多