2017-08-07 7 views
2

のパラメータ化属性に基づいてオブジェクトのリスト:その属性に基づいて、このオブジェクトのリストmylistをソートするのソート我々は次の属性を持つオブジェクトを持っていると仮定すると、オブジェクト

public class MyObject { 

    private String attr1; 
    private Integer attr2; 

    //... 

    public String getAttr1() { 
     return this.attr1; 
    } 

    public Integer getAttr2() { 
     return this.attr2; 
    } 
} 

1つの方法は、attr1次のとおりです。

mylist.sort(Comparator.comparing(MyObject::getAttr1)); 

それは、動的な方法でメソッド内でこのコードを使用すると、その名前に基づいてオブジェクトの属性のゲッターを返すメソッドを持つgetAttr1部品を交換することは可能ですか?以下のような何か:

public void sortListByAttr(List<MyObject> list, String attr) { 
    list.sort(Comparator.comparing(MyObject::getGetterByAttr(attr))); 
} 

MyObject::getGetterByAttr(attr)部分がをコンパイルしていませんが、私は次のコードnew PropertyDescriptor(attr, MyObject.class).getReadMethod().invoke(new MyObject())とメソッドを実装しようとした

私の考えを説明するためだけの例として、それを書いたが、それはまだ可能ではありませんcomparing方法

答えて

6

からパラメータを持つメソッドを呼び出すためにあなたは

public static Function<MyObject,Object> getGetterByAttr(String s) { 
    switch(s) { 
     case "attr1": return MyObject::getAttr1; 
     case "attr2": return MyObject::getAttr2; 
    } 
    throw new IllegalArgumentException(s); 
} 
のようなメソッドを追加することができますあなたのクラスに210

が、返された機能、それはU extends Comparable<? super U>を満たすタイプを期待してStringIntegerのそれぞれが個々の呼び出しでこの制約を満たすことができるである一方で、ジェネリックを宣言する方法がないよう、Comparator.comparingには適していません戻り値の型はgetGetterByAttrで、両方の型を許可し、comparingの宣言とまだ互換性があります。

また、Comparatorの完全な工場もあります。これは、それはまたタイプComparableではなく、カスタムComparatorを必要とするプロパティをサポートできるという利点を持っている

public void sortListByAttr(List<MyObject> list, String attr) { 
    list.sort(getComparator(attr)); 
} 

のように使用する

public static Comparator<MyObject> getComparator(String s) { 
    switch(s) { 
     case "attr1": return Comparator.comparing(MyObject::getAttr1); 
     case "attr2": return Comparator.comparing(MyObject::getAttr2); 
    } 
    throw new IllegalArgumentException(s); 
} 

。また、より効率的なプリミティブ型のコンパレータ(例えば、comparingIntを使用)も可能である。

またMap代わりのswitchを使用して検討してください、

private static Map<String,Comparator<MyObject>> COMPARATORS; 
static { 
    Map<String,Comparator<MyObject>> comparators=new HashMap<>(); 
    comparators.put("attr1", Comparator.comparing(MyObject::getAttr1)); 
    comparators.put("attr2", Comparator.comparing(MyObject::getAttr2)); 
    COMPARATORS = Collections.unmodifiableMap(comparators); 
} 
public static Comparator<MyObject> getComparator(String s) { 
    Comparator<MyObject> comparator = COMPARATORS.get(s); 
    if(comparator != null) return comparator; 
    throw new IllegalArgumentException(s); 
} 

よりダイナミックではリフレクション経由でのみ可能であるが、これは、コードを複雑に潜在的なエラー源の多くを追加し、少しだけ利益となり上記の例のいずれかで、別のプロパティのサポートを追加するためのソースコードを1行追加するだけでよいことを考慮してください。結局のところ、定義されたプロパティのセットはコンパイル時に固定されます。

3

はまた、このコンパレータが定義される単一の場所かもしれない:

static enum MyObjectComparator { 

    ATTR1("attr1", Comparator.comparing(MyObject::getAttr1)); 

    MyObjectComparator(String attrName, Comparator<MyObject> comparator) { 
     this.comparator = comparator; 
     this.attrName = attrName; 
    } 

    private final Comparator<MyObject> comparator; 

    private final String attrName; 

    private static MyObjectComparator[] allValues = MyObjectComparator.values(); 

    public static Comparator<MyObject> findByValue(String value) { 
     return Arrays.stream(allValues) 
       .filter(x -> x.attrName.equalsIgnoreCase(value)) 
       .map(x -> x.comparator) 
       .findAny() 
       .orElseThrow(RuntimeException::new); 
    } 

} 

をそして、あなたの使い方は次のようになります。

public void sortListByAttr(List<MyObject> list, String attr) { 
    list.sort(MyObjectComparator.findByValue(attr)); 
} 
+0

まず、upvoteすべてを。私もenumを使ってそれをやりたい'findByValue'の名前を' by'に変更すると、もっと意味があると思います。 –

+1

@ holi-javaこれは実際に私が多くのプロジェクト( 'findBy')、主に春に見たパターンです - それで僕はそれに慣れています – Eugene

+0

ええと、具体的に列挙型を導入して、 OOの方法で。 'sort(by(" attr1 ").company)'または 'process(by(" attr1 ")。getter)'のように書くことができます。 –

関連する問題