2015-10-01 14 views
5

Ultimamente sto lavorando con Java e mi chiedevo se esistesse alcun tipo di derivazione di implementazione dell'interfaccia in Java. Il mio linguaggio di programmazione preferito è Haskell, che per molti versi è antitetico a Java, ma una caratteristica che mi chiedevo se Java avesse qualcosa di simile è la capacità di derivare implementazioni di interfaccia di tipi composti dalle implementazioni dell'interfaccia dei loro parametri. Ad esempio, in Haskell:Derivazione dell'interfaccia in Java

data Pair k v = Pair k v 

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

Questo consente di ordinare un Pair se primo parametro può essere ordinato, senza richiedere esplicitamente che essere il caso. Tuttavia, il più vicino posso venire a questo in Java è attraverso requisito esplicito:

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

Senza un modo di lasciare la comparabilità può dedurre, è impossibile per me implementare un BST di coppie senza garantire che tutte le coppie avere un primo elemento paragonabile, quindi non posso implementare nessun tipo di mappa in cui il primo elemento non sia esplicitamente richiesto per essere comparabile. Esiste un modo per aggirare questo, oltre a creare un metodo della mia classe BST che tenta di confrontare il tipo generico col primo come paragonabile, e poi come una coppia con una chiave paragonabile, confrontando ogni volta che la comparabilità è possibile?

+5

stessa domanda? http://stackoverflow.com/questions/32789437/constrained-interface-implementation – ZhongYu

+1

grazie per avermi collegato a questo. sembra che questa osservazione sia comune tra le persone che arrivano a Java con alcune esperienze FP. – archaephyrryx

risposta

4

Non è possibile. Java non ha un meccanismo di classe del tipo; Le interfacce di Java assomigliano vagamente a classi di tipi, ma non hanno alcun mezzo per implementare automaticamente un'interfaccia (tra le altre, differenze più fondamentali rispetto alle classi di tipi). Un tipo implementa o non implementa un'interfaccia - non c'è modo di rendere un tipo magicamente ereditario in fase di compilazione qualcosa che già non lo è, né fare in modo che un tipo smetta di ereditare qualcosa che già non lo è.

Inoltre, non è possibile spostare il vincolo su compareTo, poiché ciò dovrebbe avvenire alla definizione di compareTo entro Comparable.

Sarei felice che qualcuno mi dimostrasse che non sbaglio.

Il più vicino alle classi di tipi di stile Haskell sulla JVM sono Scala e Frege - Il meccanismo di risoluzione implicita di Scala è in qualche modo ancora più potente/espressivo di Haskell ma anche più dettagliato e presenta alcune limitazioni fondamentali WRT l'uso di elementi esistenziali e digita i vincoli della classe insieme; Frege è solo un clone di Haskell per la JVM.


Soluzione:

parte un Java-specifica/specifiche problema, possibilmente pattern-based e parziale, soluzione, il più vicino io sappia è esplicito reimplementazione di tipo classi (che è solo un termine/implementazione specifico Haskell di ad-hoc polymorphism).

La domanda collegata da bayou.io consente di iniziare (anche se è abbastanza semplice in ogni caso): Constrained interface implementation. Anche la derivazione di istanza di classe del tipo dovrà avvenire in modo esplicito - non otterrai alcun livello di tipo, la magia di computazione automatica del tempo di compilazione che ottieni in Haskell o Scala o simile, ma ottieni comunque la flessibilità e (la maggior parte?) Statica controllo.

P.S. alcuni Haskellers calcolano che è come dovrebbe essere fatto anche in Haskell, quindi questo potrebbe darvi un'idea o tre: http://www.haskellforall.com/2012/05/scrap-your-type-classes.html.

0

È possibile creare due distinte classi Pair come questo:

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

e limitare i vostri alberi paragonabile alla seconda classe. Tuttavia sembra più pulito (soprattutto in Java-8) per utilizzare coppia non comparabili, ma specificare il Comparator invece:

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) ... 
} 

questo modo a raggiungere una maggiore flessibilità, come si può confrontare le chiavi non solo di ordine naturale, ma da qualsiasi ordine ti piaccia Se hai bisogno di un ordine naturale, usa solo new MyTree<String, Integer>(Comparator.naturalOrder());. In questo modo avrai un controllo in fase di compilazione per verificare se il tuo tipo di chiave è comparabile.

2

In Java, è spesso meglio avere un tipo di interfaccia o classe "companion" per questo tipo di problema. In un certo senso, questi compagni sono molto più vicini alle classi di tipo Haskell di qualcosa costruito con l'ereditarietà. Per il confronto, questo "compagno" è Comparator. Un vantaggio è che puoi ordinare una classe (per esempio Persona) con diversi comparatori (diciamo uno per nome, uno per età ...). Nel tuo caso, l'approccio "compagno" risolve anche il problema "generici deve essere più specifico per questo lavoro":

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