2013-09-23 97 views
5

Tôi đang cố gắng để gọi phương thức set này ghi nhận here, trong thư viện Java jOOQ, với chữ ký:Tại sao Scala không sử dụng chuyển đổi ngầm ở đây?

<T> ... set(Field<T> field, T value) 

dòng Scala Đây là một vấn đề:

.set(table.MODIFIED_BY, userId) 

MODIFIED_BY là một Field<Integer> đại diện cho cột bảng. userIdInt. Predef có chuyển đổi tiềm ẩn từ Int đến Integer, vậy tại sao nó không sử dụng nó? Tôi có được điều này: Cập nhật

type mismatch; found: org.jooq.TableField[gen.tables.records.DocRecord,Integer]  
      required: org.jooq.Field[Any] 
Note: Integer <: Any 
(and org.jooq.TableField[gen.tables.records.DocRecord,Integer] <: 
    org.jooq.Field[Integer]), but Java-defined trait Field is invariant in type T. 
You may wish to investigate a wildcard type such as `_ <: Any`. (SLS 3.2.10) 

- Giới thiệu về Ví dụ Vinicius của

Thay vì cố gắng giải thích điều này trong ý kiến, đây là một minh chứng rằng không có chuyển đổi ngầm được gọi là khi bạn sử dụng một loại với hiệp biến tham số, như List[+T]. Hãy nói rằng tôi đặt mã này trong một tập tin, biên dịch, và chạy nó ...

case class Foo(str: String) 

object StackOver1 extends App { 

    implicit def str2Foo(s: String): Foo = { 
    println("In str2Foo.") 
    new Foo(s) 
    } 

    def test[T](xs: List[T], x: T): List[T] = { 
    println("test " + x.getClass) 
    xs 
    } 

    val foo1 = new Foo("foo1") 
    test(List(foo1), "abc") 
} 

Bạn sẽ thấy rằng nó gọi kiểm tra, nhưng không bao giờ chuyển đổi ngầm từ String "abc" để Foo. Thay vào đó, hãy chọn một số T cho test[T] là lớp cơ sở chung giữa StringFoo. Khi bạn sử dụng IntInteger nó chọn Any, nhưng điều này gây nhầm lẫn vì biểu diễn thời gian chạy của Int trong danh sách là Integer. Vì vậy, có vẻ như nó đã sử dụng chuyển đổi ngầm, nhưng nó không. Bạn có thể xác minh bằng cách mở lời nhắc Scala ...

scala> :type StackOver1.test(List(new java.lang.Integer(1)), 2) 
List[Any] 

Trả lời

2

Tôi không biết gì về jOOQ, nhưng tôi nghĩ vấn đề là Scala không hiểu Generics java rất tốt. Thử:

scala> def test[T](a : java.util.ArrayList[T], b: T) = { println(a,b) } 
scala> val a = new java.util.ArrayList[Integer]() 
scala> val b = 12 
scala> test(a,b) 
<console>:11: error: type mismatch; 
found : java.util.ArrayList[Integer] 
required: java.util.ArrayList[Any] 
Note: Integer <: Any, but Java-defined class ArrayList is invariant in type E. 
You may wish to investigate a wildcard type such as `_ <: Any`. (SLS 3.2.10) 
       test(a,b) 

Có vẻ quen thuộc không ??

Và để khắc phục, chỉ cần thông báo cho loại T để gọi phương thức: test[Integer](a,b) hoạt động tốt.

EDIT:

Có một vài điều liên quan như:

  1. Erasure -> Khi biên soạn các loại hình chung sẽ biến mất bởi tẩy xoá. Trình biên dịch sẽ sử dụng Object mà Scala, sẽ coi là Any. Tuy nhiên một ArrayList [Integer] không phải là một ArrayList [Any], mặc dù Integer là bất kỳ. Giống như cách mà TableField [gen.tables.records.DocRecord, Integer] không phải là một trường [Any].

  2. Cơ chế suy luận loại -> nó sẽ tìm ra loại T nên và để làm điều đó nó sẽ sử dụng phần tử giao nhau của các loại được truyền (trong trường hợp của chúng ta là tổ tiên chung đầu tiên). Trang 36 của Scala Language Spec, trong ví dụ trên của chúng tôi sẽ dẫn đến sử dụng Bất kỳ.

  3. Chuyển đổi ngầm định -> đó là bước cuối cùng và sẽ được gọi nếu có loại chuyển đổi sang loại khác, nhưng vì loại đối số được xác định là tổ tiên chung đầu tiên, không cần để chuyển đổi và chúng tôi sẽ không bao giờ có một chuyển đổi ngầm nếu chúng ta không buộc các loại T.

một ví dụ để chứng tỏ tổ tiên chung được sử dụng để xác định T:

scala> def test[T](a: T, b: T): T = a 
scala> class Foo 
scala> class Boo extends Foo 
scala> test(new Boo,new Foo) 
res2: Foo = [email protected] 
scala> test(new Boo,new Boo) 
res3: Boo = [email protected] 
scala> class Coo extends Foo 
scala> test(new Boo,new Coo) 
res4: Foo = [email protected] 
scala> test(new Boo,"qsasad") 
res5: Object = [email protected] 

Tổng hợp , phương thức ngầm không được gọi, bởi vì loại cơ chế suy luận, xác định các loại trước khi nhận được đối số và vì nó sử dụng tổ tiên chung, không có nhu cầu cho một chuyển đổi tiềm ẩn.

Mã của bạn tạo ra lỗi do cơ chế xóa bỏ biến mất cùng với thông tin loại quan trọng để xác định loại đối số chính xác.

@RobN, cảm ơn vì đã đặt câu hỏi cho câu trả lời của tôi, tôi đã học được rất nhiều về quy trình.

+0

OK, cảm ơn. Tôi hiểu sự khác biệt không chính xác giữa Danh sách và ArrayList, nhưng tiếc là thêm [Integer] không tốt trong trường hợp này. Nó sẽ xảy ra quá nhiều và làm cho mã xấu xí. Tôi muốn biết tại sao ngầm định không được sử dụng. –

+0

Tôi sẽ chấp nhận câu trả lời này nếu tôi có thể tìm thấy nội dung nào đó trong tài liệu hoặc thông số ngôn ngữ cho biết lý do tại sao nó không xem xét tiềm ẩn. Có lẽ tất cả các thông số kiểu được quyết định trước khi xem xét các hàm ý. Nếu không phải như vậy, nó có thể sử dụng 'ArrayList [Integer]' thay vì 'ArrayList [Any]'. Ví dụ bạn không hoàn toàn đúng ... nó không "ép buộc chuyển đổi ngầm". Thật khó hiểu trong trường hợp này bởi vì scala đang sử dụng 'Integer' khi chạy vì nó là' Int', nhưng hàm test của bạn đơn giản là nhận được một 'List [Any]'. Không có hàm chuyển đổi ngầm nào được gọi. –

+0

@RobN, hãy giúp tôi cải thiện câu trả lời của bạn. Hãy xem nếu tôi có thể làm cho nó rõ ràng. Ví dụ đầu tiên tái tạo lỗi của bạn. Trình biên dịch doesnt biết làm thế nào để phù hợp (javageneric, Int) vào (javageneric , T). Điều thứ hai trong Scala cho thấy sự chuyển đổi ngầm xảy ra, chú ý rằng b được chuyển đổi thành java.lang.Integer. Tôi không biết nơi này là trong tài liệu hướng dẫn, tôi chỉ cho bạn thấy bằng cách thử nghiệm. Nếu bạn có một ví dụ có thể chứng minh cho tôi sai, xin vui lòng làm. –