2016-03-10 10 views
8

ここはひどいナットです。私は、varargsとジェネリックスを一緒に使用することの間に衝突があります。与えられたコードを次に示します。Javaでの連鎖比較のための可変引数とジェネリックの結合

public class MyObject implements Comparable<MyObject> 
{ 
    private String name; 
    private int index; 

    @Override 
    public int compareTo(MyObject o) 
    { 
     if (name.compareTo(o.name) != 0) 
      return name.compareTo(o.name); 
     return ((Integer) index).compareTo(o.index); 
    } 
} 

私はcompareTo方法は、複数の条件を比較使用したいです。文字列が同じ場合は、代わりにintを使用します。私が言う通常の状況。
これを一般的に処理する静的メソッドを作成したいと思います。そして、私はこのように呼ばれる新しい方法chainedCompareたい:

public int compareTo(MyObject o) 
{ 
    return chainedCompare(this, o, myO -> myO.name, myO -> myO.index); 
} 

をラムダは、Java 8のインターフェイス機能の可変引数です。だから、最初に私はそのような方法を書いた:

public static <T, C extends Comparable<C>> int chainedCompare(T object1, T object2, Function<T, C>... comparisons) 
{ 
    int compareValue = 0; 
    for (Function<T, C> comparison : comparisons) 
    { 
     compareValue = comparison.apply(object1).compareTo(comparison.apply(object2)); 
     if (compareValue != 0) 
      break; 
    } 
    return compareValue; 
} 

をしかし、私はこのケースではジェネリック型Cは可変引数配列内のすべてのFunction<T, C>比較のため、同じタイプでなければならないことを考慮していませんでした。上記のように、私は異なるComparables(例ではStringとIntegerのような)を使いたいと思っています。
それから私はこのバージョンにそれを修正:

public static <T> int chainedCompare(T object1, T object2, Function<T, ? extends Comparable<?>>... comparisons) 
{ 
    int compareValue = 0; 
    for (Function<T, ? extends Comparable<?>> comparison : comparisons) 
    { 
     compareValue = comparison.apply(object1).compareTo(comparison.apply(object2)); 
     if (compareValue != 0) 
      break; 
    } 
    return compareValue; 
} 

タイプCは、ここでは、ワイルドカードに置き換えられます。メソッド呼び出しは現在動作しますが、ワイルドカード型のパラメータcompareToのため、メソッド自体はコンパイルされません。

したがって、私は、汎用インターフェイス(汎用インターフェイス)の固定汎用型が必要ですが、一方で、通常はワイルドカードを設定できる別の(第2)汎用タイプの関数インターフェイスが必要です。これを解決するには?
私の唯一の要件は、定義されていない数の比較条件で示されているように静的メソッドを単純なものとして呼び出すことができることです。私は希望のように使用することができ、次のようにメソッドを変更することができましたTunakiの提案に基づいて


:代わりにComparableを使用しての

@SuppressWarnings("raw-types") 
public static <T> int chainedCompare(T object1, T object2, Function<T, ? extends Comparable>... comparisons) 
{ 
    return Arrays.stream(comparisons) 
     .map(Comparator::comparing) 
     .reduce(Comparator::thenComparing) 
     .map(c -> c.compare(object1, object2)) 
     .orElse(0); 
} 

public int compareTo(MyObject o) 
{ 
    return chainedCompare(this, o, myO -> myO.name, myO -> myO.index); 
} 

答えて

4

Comparatorを使用するために容易になるだろう:

public static <T> int chainedCompare(T object1, T object2, Comparator<T>... comparators) { 
    int compareValue = 0; 
    for (Comparator<? super T> comparator : comparators) { 
     compareValue = comparator.compare(object1, object2); 
     if (compareValue != 0) 
      break; 
    } 
    return compareValue; 
} 

またthenComparingを使用して一緒にすべてのコンパレータをチェーンと

を持つことができます
@SafeVarargs 
public static <T> int chainedCompare(T object1, T object2, Comparator<T>... comparators) { 
    return Arrays.stream(comparators) 
       .reduce(Comparator::thenComparing) 
       .map(c -> c.compare(object1, object2)) 
       .orElse(0); 
} 

オブジェクトをcomparing(keyExtractor)またはプリミティブの特殊化comparingIntで構成することで、それを使用できます。このアプローチで

@Override 
public int compareTo(MyObject o) { 
    return chainedCompare(this, o, 
      Comparator.comparing(obj -> obj.name), 
      Comparator.comparingInt(obj -> obj.index) 
      ); 
} 

、あなたも、このようなユーティリティの存在を疑問視し、単に素敵なアプローチである

@Override 
public int compareTo(MyObject o) { 
    return Comparator.<MyObject, String> comparing(obj -> obj.name) 
        .thenComparingInt(obj -> obj.index) 
        .compare(this, o); 
} 
+0

を持つことができます。 Comparator.comparing()をそれぞれの条件ごとに別々に書く必要がなければ、もっと良いでしょう。関数comparison()も関数をとるので、関数varargsを保持し、コンパレータをchainedCompareメソッド内に構築することは涼しいでしょう。しかし、私は以前と同じような一般的な問題を抱えていたと思いますよね?それは解決できませんか? – Arceus

+0

@Arceusはい、あなたは同じ問題に陥ります。私は別の簡単な解決法で編集したことに注意してください。 – Tunaki

+0

あなたの提案は可能な解決策です。この時点でありがとう。しかし、私の本当の問題は、compareToの内容をonelinerにするだけでなく、可能な限り短くすることでした。そして正直なところ、あなたのコードは少し冗長であることがわかります。オーバーライドされたすべてのcompareTo(これは非常に多くになる)のように書く必要があるからです。 – Arceus

関連する問題