2015-10-01 13 views
5

私はJavaの開発に遅れをとっており、Javaでインタフェース実装の導出があったのかどうか疑問に思っていました。私の好みのプログラミング言語はHaskellです。これはJavaに対して多くの点で相反していますが、Javaのようなものがあるかどうか疑問に思っていた機能の1つは、複合型のインタフェース実装をパラメータのインタフェース実装から派生させることです。例えば、Haskellで:Javaでのインタフェースの導出

data Pair k v = Pair k v 

instance (Ord k) => Ord (Pair k v) where 
    compare (Pair x _) (Pair x' _) = compare x x' 

これは、その最初のパラメータが明示的にケースのようにそれを必要とせずに、注文することができる場合は、Pairを注文することができます。しかし、私はJavaでこれに来ることができる最も近いが、明示的な要件によって次のとおりです。私はすべてのペアことを確認せずにペアのBSTを実装するために推論する比較可能性を残す方法がなければ

class Pair<K extends Comparable<K>, V> extends Comparable<Pair<K,V>> { 
    K k; 
    V v; 
    public int compareTo(Pair<K,V> p) { 
     return k.compareTo(p.k); 
    } 
} 

、それは不可能です最初の要素がComparableであることが明示的に要求されていないMapは実装できません。これを回避する方法はありますか?BSTクラスのメソッドを作成して、ジェネリック型を比較可能なものとして最初にキャストし、次に比較可能なキーを持つペアとして比較可能であるときはいつでも比較しようとする方法以外はありますか?

+5

同じ質問? http://stackoverflow.com/questions/32789437/constrained-interface-implementationこれに私をリンクするための – ZhongYu

+1

感謝。このような観察は、JavaにいくつかのFP経験を持って来る人々の間で一般的であるようです。 – archaephyrryx

答えて

4

これはできません。 Javaには型クラスメカニズムがありません。 Javaのインタフェースは単に型クラスに似ているだけですが、インタフェースを自動実装する手段がありません(他にも、型クラスとの基本的な違いがあります)。型はインターフェースを実装するか実装しません。既にタイプしていないものをコンパイル時に魔法のように継承することはできません。また、すでにタイプしていないタイプを継承しないこともできません。

制限をcompareToに移動することもできません。これは、compareToの定義で行われる必要があります。Comparableです。

私は誰かがこれについて間違っていることを証明することができてうれしいです。

JVM上のHaskellスタイルの型クラスに最も近いのはScalaとFregeです - Scalaの暗黙的な解決メカニズムは何らかの形でHaskellよりも強力で表現力がありますが、より冗長であり、いくつかの基本的な制限があります。クラスの制約を一緒に入力します。 FregeはHaskellのJVMへのクローンです。

別にJava固有/問題固有の、おそらくパターンベースと部分から、この問題を回避するには、私の知る最も近いタイプの明示的な再実装です:


回避策クラス(これはHaskell固有の用語/実装であるad-hoc polymorphismです)。

bayou.ioにリンクされている質問は、(とにかく簡単ですが)開始します:Constrained interface implementation。型のクラスインスタンスの導出も明示的に行う必要があります。型レベル、HaskellやScalaなどで得られるコンパイル時の自動計算の魔法を得ることはできませんが、やはり柔軟性と静的なチェック。

P.S.いくつかのハスケラーはそれがハスケルでさえどのように行われるべきなのかを考えているので、これはあなたに考えか3を与えるかもしれません:http://www.haskellforall.com/2012/05/scrap-your-type-classes.html

public class Pair<K, V> { 
    protected final K key; 
    protected final V value; 

    public Pair(K key, V value) { 
     this.key = key; 
     this.value = value; 
    } 
} 

public class ComparablePair<K extends Comparable<K>, V> extends Pair<K, V> implements 
     Comparable<ComparablePair<K, V>> { 

    public ComparablePair(K key, V value) { 
     super(key, value); 
    } 

    @Override 
    public int compareTo(ComparablePair<K, V> o) { 
     return key.compareTo(o.key); 
    } 
} 

をそして第​​二のクラスにご匹敵する樹木を制限:

0

あなたは、このように二つの異なるPairクラスを作成することができます。あなたが自然な順序によって、しかしではないキーだけを比較することができて、あなたがより多くの柔軟性を達成するため、この方法を

class MyTree<K, V> { 
    final Comparator<K> comparator; 

    public MyTree(Comparator<K> comparator) { 
     this.comparator = comparator; 
    } 

    ... instead of k1.compareTo(k2) use comparator.compare(k1, k2) ... 
} 

を:しかし、それは非同等のペアを使用しますが、代わりにComparatorを指定するには、(特にJavaの-8で)きれいに見えますあなたが好きな注文。あなたが自然な順序が必要な場合は、単にnew MyTree<String, Integer>(Comparator.naturalOrder());を使用しています。このようにして、コンパイル時にキーの型が同等かどうかをチェックします。 Javaでは

2

、この種の問題のための「仲間」のインターフェイスまたはクラスのようなものを持っているほうが良い場合が多いです。ある意味では、これらの仲間は、継承で構成何かよりHaskellの型クラスに非常に近いです。比較ですについては、この「仲間」Comparatorです。一つの利点は、あなたが別のコンパレータ(名の1、年齢のための1つを...と言う)と1つのクラスを(人を言う)ソートすることができるということです。あなたのケースでは、 "コンパニオン"アプローチは、 "ジェネリックはこれが機能するためにはより具体的でなければなりません"という問題を解決します:

public class Pair<K, V> { 
    final public K k; 
    final public V v; 

    public Pair(K k, V v) { 
     this.k = k; 
     this.v = v; 
    } 
} 

public class PairComparator<K extends Comparable<? super K>, V> 
    implements Comparator<Pair<K,V>> { 

    @Override 
    public int compare(Pair<K, V> o1, Pair<K, V> o2) { 
     return o1.k.compareTo(o2.k); 
    } 
} 

//works for any Pairs with comparable keys: 
PairComparator<String, Integer> comp = 
    new PairComparator<String, Integer>(); 
Pair<String, Integer> p1 = new Pair<>("z",1); 
Pair<String, Integer> p2 = new Pair<>("a",3); 
System.out.println(comp.compare(p1,p2)); 
関連する問題