codice Java evitare la duplicazione quando ottenere un elenco di un immobile in ogni oggetto da un elenco di oggetti

StackOverflow https://stackoverflow.com/questions/2195074

  •  25-09-2019
  •  | 
  •  

Domanda

Ho diversi oggetti che assomigliano a questo:

class PhoneNumber
{
   String   getNumber();
   String   getExtension();
   DateTime getLastCalled();
}
class Address
{
   String getCity();
   string getState();
   int    getZip();
}

Mi piacerebbe essere in grado di prendere un elenco di uno qualsiasi di questi oggetti e ottenere un elenco di una particolare proprietà. Questa è un'operazione banale, se dovessi scrivere una funzione per ogni proprietà, ma a quanto mi risulta, è cattiva pratica di codice duplicato tante volte.

Così mi piacerebbe essere in grado di fare qualcosa di simile per qualsiasi proprietà in entrambe oggetto:

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

ho travasato il mio cervello cercando di fare questo con i generici. Io non sono nemmeno sicuro che questo è possibile, ma se ho capito bene, un sacco di cose magiche si possono fare con il linguaggio di programmazione Java.

È stato utile?

Soluzione

Si può fare questo con i generici e una classe di utilità per fare il lavoro. Mi piace questo un po 'meglio che usare riflessione perché si ottiene il tempo di compilazione controllando che la proprietà si desidera recuperare esiste (e non è errata, ecc.).

Per prima cosa una classe che fa la conversione vera e propria. Si noti l'astratto "get" metodo che abbiamo sovrascriveremo in una classe interna anonima quando chiamiamo 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;
  }
}

Poi si utilizza in questo modo. Non una fodera una bella, ma il tempo di compilazione verifica la bontà:

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

Altri suggerimenti

Per qualcosa di questo semplice, si sta meglio solo fornendo i getter e setter. Non sta salvando molto rifiutando di duplicare una riga di codice. Ancora meglio, basta ottenere il vostro IDE per scrivere per voi.

È in grado di gestire i vostri oggetti con Introspector come in questo SO risposta .

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

Poi si può fare una chiamata in questo modo:

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

È possibile utilizzare la riflessione, per ottenere i nomi dei metodi (java.lang.Class e java.lang.reflect *.); quelli che iniziano con "get" sono le proprietà, il cui nome segue "get".

Questo è un buon caso per le chiusure in Java; Sta arrivando, ma non è ancora arrivato.

Nel frattempo, si può fare questo con la riflessione, anche se ha sempre riflesso una riduzione delle prestazioni. Una delle cose riflessione può fare è di lasciare che si chiama un metodo su un oggetto, o di recuperare una proprietà, specificando il nome del metodo / proprietà come una stringa; nel tuo caso si vorrebbe fare getListOfProperties(AddressList, "city") e all'interno di tale metodo di utilizzare la reflection per recuperare la proprietà specificata.

Questo tipo di cosa è semplificata notevolmente utilizzando la href="http://commons.apache.org/beanutils/" rel="nofollow noreferrer"> biblioteca .

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;
}
scroll top