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 đề.
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? –
Có thể. Hãy để tôi có một vết nứt ở đó. –
Đã thêm một lớp Java và đầu ra REPL cho thấy sự khác biệt. –