2013-02-11 32 views
9

Các generics Java có thể suy ra kiểu tham số kiểu chung dựa trên kiểu trả về của các biểu thức. Hãy xem xét những điều sau đây:Scala và Java Generics - Giải nén và trả về các kiểu lồng nhau

public static <T> T uncheckedCast(Object o) { 
    return (T)o; 
} 

Chúng ta có thể gọi nó như:

Map<Baz,Bog> bazbogMap = new HashMap<Baz,Bog>(); 
String foo = uncheckedCast(bazbogMap); 

này sẽ biên dịch nhưng ném một RuntimeException khi nó được gọi, vì nó sẽ cố gắng cast Map đến một String nhưng thất bại . Nhưng vấn đề là Java suy ra giá trị của <T> dựa trên loại kết quả dự kiến ​​tại callite.

Chúng tôi cũng có thể làm điều này trong Scala với:

def uncheckedCast[T](o: AnyRef): T = o.asInstanceOf[T] 

Cho đến nay, như vậy tốt.

Java cũng có thể suy ra luận cứ loại từ các định nghĩa lồng nhau và trả lại những (tức là chúng ta không thực sự phải gán kết quả vào một loại để sử dụng nó như trên trước khi sử dụng nó;. Java biết những gì nó đã)

Một lớp học đơn giản cho thấy điều này:

import java.util.HashMap; 
import java.util.Map; 

public class KeysAndValues { 
    public interface Key<T> {} 
    public static class StringKey implements Key<String> {} 

    private final Map<Class<?>, Object> lookup; 

    public KeysAndValues() { 
     lookup = new HashMap<Class<?>, Object>(); 
    } 

    @SuppressWarnings("unchecked") 
    public <V, K extends Key<V>> V put(Class<K> key, V value) { 
     return (V) lookup.put(key, value); 
    } 

    @SuppressWarnings("unchecked") 
    public <V, K extends Key<V>> V get(Class<K> key) { 
     return (V) lookup.get(key); 
    } 

    public static void test() { 
     KeysAndValues kv = new KeysAndValues(); 
     kv.put(StringKey.class, "BAM!"); // returns null 
     kv.put(StringKey.class, "BOOM!"); // returns "BAM!" 
     kv.get(StringKey.class); // returns "BOOM!" 
    } 
} 

Tuy nhiên, trong Scala lớp này gây ra sự cố. REPL:

scala> val kv = new KeysAndValues 
kv: KeysAndValues = [email protected] 

scala> import KeysAndValues.StringKey 
import KeysAndValues.StringKey 

scala> kv.put(classOf[StringKey], "BAM!") 
res0: java.lang.String = null 

scala> kv.put(classOf[StringKey], "BOOM!") 
res1: java.lang.String = BAM! 

scala> kv.get(classOf[StringKey]) 
<console>:10: error: inferred type arguments [Nothing,KeysAndValues.StringKey] do not conform to method get's type parameter bounds [V,K <: KeysAndValues.Key[V]] 
       kv.get(classOf[StringKey]) 

Hai cuộc gọi đến put() định giá trị cho các tham số V loại cho phép họ để làm việc, nhưng ngay sau khi các thông số V cần phải được chiết xuất từ ​​các thừa kế của tham số K, mọi thứ phá vỡ xuống. Mã Java không có vấn đề như vậy.

Cách duy nhất để có được Scala không khiếu nại được xác định một cách rõ ràng các loại:

kv.get[String, StringKey](classOf[StringKey]) 

(Đối với một cái gì đó đơn giản và contrived như ở trên nó nhẹ ổn (vi phạm DRY), nhưng đối với một cái gì đó tham gia nhiều hơn, như The Stanford lõi NLP API, nơi bạn phải làm một cái gì đó như:

doc.get[java.util.Map[Integer, CorefChain], CorefChainAnnotation](classOf[CorefChainAnnotation]) 

trong đó bao gồm bằng tay nhìn lên các loại lồng nhau, do đó bạn có thể thêm chúng, là khá đau)

.

(Các) Câu hỏi:

Có cách nào để làm việc này mà không cần phải chỉ định các thông số loại sao cho rõ ràng không? Quan trọng hơn, tại sao đây lại là vấn đề để bắt đầu? Làm thế nào là Java có thể suy ra các đối số loại khi Scala không?

CHỈNH SỬA 1: Đã xóa hầu hết mã trình bày vấn đề NLP của Stanford Core và thay thế bằng ví dụ chung về sự khác biệt về tướng Scala/Java minh họa vấn đề.

+0

Bạn có thể sao chép sự cố của mình trong mã Java và Scala thuần túy có thể biên dịch mà không có thư viện bên ngoài không? –

+0

Có thể. Hãy để tôi có một vết nứt ở đó. –

+0

Đã thêm một lớp Java và đầu ra REPL cho thấy sự khác biệt. –

Trả lời

3

Tôi không thể cho bạn biết chính xác lý do tại sao Scala không thể suy ra các thông số loại Java, nhưng tôi có thể ít nhất là cho bạn thấy làm thế nào để khai báo các tham số kiểu trong Java để Scala thể suy ra chúng:

@SuppressWarnings("unchecked") 
public <V> V put(Class<? extends Key<V>> key, V value) { 
    return (V) lookup.put(key, value); 
} 

@SuppressWarnings("unchecked") 
public <V> V get(Class<? extends Key<V>> key) { 
    return (V) lookup.get(key); 
} 

Tôi nghĩ điểm mấu chốt ở đây là bạn không cần cần rằng thông số loại K hoàn toàn - thông số loại ký tự đại diện, ? extends Key<V> sẽ cung cấp cho bạn chức năng tương tự. Điều này sau đó làm việc vui vẻ từ Scala mà không cần phải chỉ định bất kỳ thông số type:

scala> kv.put(classOf[StringKey], "BAM!") 
res0: String = null 

scala> kv.get(classOf[StringKey]) 
res1: String = BAM! 

EDIT:

Dưới đây là tốt nhất tôi có thể làm cho một workaround. Nó đòi hỏi các loại cao hơn và một diễn viên. Không chắc chắn rằng tôi có thể giải thích lý do tại sao biên dịch này, nhưng phiên bản khác không mặc dù. ;-)

scala> import KeysAndValues._ 
import KeysAndValues._ 

scala> import scala.language.higherKinds 
import scala.language.higherKinds 

scala> def get[V, K[X] <: Key[X]](kv: KeysAndValues, key: Class[_ <: K[V]]) = 
    | kv.get[V, K[V]](key.asInstanceOf[Class[K[V]]]) 
get: [V, K[X] <: KeysAndValues.Key[X]](kv: KeysAndValues, key: Class[_ <: K[V]])V 

scala> val kv = new KeysAndValues 
kv: KeysAndValues = [email protected] 

scala> kv.put(classOf[StringKey], "BAM!") 
res0: String = null 

scala> get(kv, classOf[StringKey]) 
res1: String = BAM! 
+0

Đây là câu trả lời một phần tốt. Ví dụ của tôi được lấy từ Stanford Core NLP, nơi tôi không thể chỉnh sửa nguồn. Tôi vẫn muốn biết nếu có một Scala chỉ workaround (không?) Và tại sao sự khác biệt này tồn tại giữa Scala/Java. –

+0

Tôi liên lạc với các nhân viên NLP của Stanford Core NLP về vấn đề này, và tôi tin rằng Stanford Core NLP 1.3.5, bây giờ họ sử dụng các generics đơn giản mà tôi đã đề xuất ở phần đầu của câu trả lời của tôi. Vì vậy, bạn có thể thử nâng cấp lên Stanford Core NLP 1.3.5 và xem điều đó có giải quyết được vấn đề không. – Steve

+0

Đó là tin tuyệt vời từ quan điểm khả năng sử dụng; Tôi vẫn thực sự muốn biết tại sao Java và Scala lại có sự khác biệt này. –

Các vấn đề liên quan