2015-10-01 26 views
5

Tôi đang làm việc với Java về muộn và đã tự hỏi liệu có bất kỳ loại dẫn xuất thực hiện giao diện nào trong Java hay không. Ngôn ngữ lập trình ưa thích của tôi là Haskell, theo nhiều cách đối lập với Java, nhưng một tính năng mà tôi đã tự hỏi liệu Java có bất kỳ thứ gì giống như khả năng lấy ra các triển khai giao diện của các loại hợp chất từ ​​việc triển khai giao diện các tham số của chúng hay không. Ví dụ, trong Haskell:Dẫn xuất giao diện trong Java

data Pair k v = Pair k v 

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

này cho phép bạn đặt một Pair nếu tham số đầu tiên của nó có thể được đặt hàng, mà không đòi hỏi một cách rõ ràng rằng phải là trường hợp. Tuy nhiên, gần nhất tôi có thể đến điều này trong Java là thông qua yêu cầu rõ ràng:

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

Nếu không có một cách để rời khỏi so sánh thể suy đoán, nó là không thể cho tôi để thực hiện một BST của cặp mà không đảm bảo rằng tất cả các cặp có một phần tử đầu tiên có thể so sánh, vì vậy tôi không thể triển khai bất kỳ loại Bản đồ nào mà yếu tố đầu tiên không được yêu cầu rõ ràng là Có thể so sánh được. Có cách nào xung quanh điều này, ngoài việc tạo ra một phương thức của lớp BST của tôi cố gắng so sánh kiểu generic bằng cách đúc nó trước tiên như có thể so sánh, và sau đó là một Cặp với khóa so sánh, so sánh bất cứ khi nào có thể so sánh được?

+5

cùng một câu hỏi? http://stackoverflow.com/questions/32789437/constrained-interface-implementation – ZhongYu

+1

cảm ơn vì đã liên kết tôi với điều này. có vẻ như quan sát này là phổ biến trong số những người đến Java với một số kinh nghiệm FP. – archaephyrryx

Trả lời

4

Không thể thực hiện được. Java thiếu cơ chế lớp loại; Các giao diện của Java chỉ đơn thuần là giống các lớp kiểu, nhưng thiếu bất kỳ phương tiện nào để tự động thực hiện một giao diện (trong số các sự khác biệt cơ bản hơn từ các lớp kiểu). Một loại thực hiện hoặc không thực hiện một giao diện - không có cách nào để tạo một kiểu thừa kế kỳ diệu vào thứ gì đó mà nó đã không có, cũng không tạo kiểu dừng thừa kế một cái gì đó mà nó không có.

Cũng không thể di chuyển ràng buộc xuống compareTo, vì điều đó sẽ phải xảy ra ở định nghĩa compareTo trong phạm vi Comparable.

Tôi rất vui vì ai đó đã chứng minh cho tôi sai về điều này. Gần nhất bạn có được với các kiểu lớp kiểu Haskell trên JVM là Scala và Frege - Cơ chế giải quyết ngầm của Scala là theo một số cách mạnh mẽ hơn/biểu cảm hơn Haskell nhưng cũng tiết hơn và có một số hạn chế cơ bản WRT sử dụng các tồn tại và loại các ràng buộc lớp với nhau; Frege chỉ là một bản sao của Haskell với JVM.


Cách giải quyết:

Ngoài một Java-cụ thể/vấn đề cụ thể, có thể là mô hình dựa trên phần và bán phần, khắc phục, gần nhất một Tôi nhận thức được rõ ràng là tái thực hiện các loại các lớp (chỉ là một thuật ngữ/triển khai cụ thể của Haskell là ad-hoc polymorphism).

Câu hỏi được liên kết đến bởi bayou.io sẽ giúp bạn bắt đầu (mặc dù nó khá đơn giản): Constrained interface implementation. Loại dẫn xuất thể hiện lớp sẽ cần phải xảy ra một cách rõ ràng - bạn sẽ không nhận được bất kỳ cấp độ nào, biên dịch tự động tính toán thời gian ma thuật bạn nhận được trong Haskell hoặc Scala hoặc tương tự, nhưng bạn vẫn nhận được sự linh hoạt và (nhất?) Tĩnh kiểm tra.

P.S. một số Haskellers nghĩ rằng đó là cách nó nên được thực hiện ngay cả trong Haskell, vì vậy điều này có thể cung cấp cho bạn một ý tưởng hoặc ba: http://www.haskellforall.com/2012/05/scrap-your-type-classes.html.

0

Bạn có thể tạo ra hai biệt Pair lớp như thế này:

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

Và hạn chế cây tương đương của bạn đến lớp thứ hai. Tuy nhiên có vẻ sạch hơn (đặc biệt là trong Java-8) để sử dụng cặp phi thể so sánh được, nhưng xác định Comparator thay vì:

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

Bằng cách này bạn đạt được sự linh hoạt hơn khi bạn có thể so sánh các phím không chỉ bởi trật tự tự nhiên, nhưng bởi bất kỳ thứ tự nào bạn thích. Nếu bạn cần thứ tự tự nhiên, chỉ cần sử dụng new MyTree<String, Integer>(Comparator.naturalOrder());. Bằng cách này, bạn sẽ có một kiểm tra thời gian biên dịch cho dù loại khóa của bạn có thể so sánh được hay không.

2

Trong Java, tốt hơn nên có loại giao diện "đồng hành" hoặc lớp học cho loại sự cố này. Trong một nghĩa nào đó, những người đồng hành này gần gũi hơn với các lớp kiểu Haskell hơn là một cái gì đó được xây dựng với kế thừa. Để so sánh, "đồng hành" này là Comparator. Một lợi thế là bạn có thể sắp xếp một lớp (nói Person) với các bộ so sánh khác nhau (ví dụ: một cho tên, một cho tuổi ...). Trong trường hợp của bạn, cách tiếp cận "đồng hành" cũng giải quyết vấn đề "generics phải cụ thể hơn cho việc này":

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