2010-10-06 41 views
9

Tôi đang thiết kế một lớp API sử dụng loại trong một số trường hợp tuy nhiên tôi đã gặp phải một vấn đề với độ phân giải ngầm định. Như được hiển thị bên dưới, nếu có một đối tượng ẩn cho loại A nhưng đối tượng thuộc loại B extends A được chuyển đến phương thức, thì không thể tìm thấy đối tượng ẩn. Có cách nào để thực hiện công việc này hoặc người gọi có phải đặt các đối tượng ngầm vào phạm vi cho mỗi phân lớp không?Loại mẫu lớp trong Scala không xem xét thừa kế?

Dưới đây là một ví dụ:

class A 
class B extends A 

class T[+X] 

object T { 
    implicit object TA extends T[A] 
} 

def call[X:T](x:X) = println(x) 

// compiles 
call(new A) 
// doesn't compile 
call(new B) 

var a = new A 
// compiles 
call(a) 

a = new B 
// compiles 
call(a) 

val b = new B 
// doesn't compile 
call(b) 

này thất bại trong việc biên dịch với đầu ra sau đây:

 
/private/tmp/tc.scala:16: error: could not find implicit value for evidence parameter of type this.T[this.B] 
call(new B) 
    ^
/private/tmp/tc.scala:28: error: could not find implicit value for evidence parameter of type this.T[this.B] 
call(b) 
+0

Tôi cũng đã cố gắng thay đổi định nghĩa của cuộc gọi tới: def call [X, X2 <: X] (x: X2) (ẩn x2: T [X]) = println (x) Và điều đó không giúp được –

Trả lời

2

Hãy thử điều này:

object T { 
    implicit def TA[X <: A] = new T[X] 
} 

import T._ 

hoặc đơn giản là:

implicit def TA[X <: A] = new T[X] 
4

Các công việc sau tốt:

scala> def call[X](x: X)(implicit evidence: T[X]<:<T[X]) = println(x) 
call: [X](x: X)(implicit evidence: <:<[T[X],T[X]])Unit 

scala> call(new A) 
[email protected] 

scala> call(new B) 
[email protected] 

scala> val b = new B 
b: B = [email protected] 

scala> call(b) 
[email protected] 

Trong trường hợp biên dịch của bạn thất bại, vì def call[X:T](x:X) = println(x) được coi là call: [X](x: X)(implicit evidence$1: T[X])Unit. Để vượt qua loại phụ, bạn có thể sử dụng các ràng buộc kiểu tổng quát.

+0

Có cách nào để thực sự có được bằng chứng $ 1 không? Ví dụ giả sử T là một hàm và x là tham số. Tôi không thể gọi bằng chứng (x) –

+0

Tại sao bạn không thể? Bạn có thể gọi nó bằng tên 'bằng chứng $ 1', giống như bất kỳ tham số [ẩn] nào khác. –

+0

Nếu bạn có một số tham số kiểu trên phương thức, sẽ có bằng chứng $ 1, bằng chứng $ 2 vv: '[X, Z] (x: X) (bằng chứng ngầm $ 1: T [X], bằng chứng ngầm $ 2: T [Z]) Unit' –

7

Gọi call(new B) có nghĩa là call[B](new B)(tB) sao cho tb thuộc loại T [B] hoặc phân lớp của nó. (Một phương thức kỳ vọng đối số kiểu T chỉ có thể mong đợi T hoặc phân lớp T, ví dụ: def foo(s: String) không thể được gọi với đối số kiểu Any). T [A] không phải là loại phụ của T [B]

Để khắc phục, bạn có thể thay đổi T để được xác định T[-X]. Điều này có nghĩa rằng trình biên dịch sẽ xem xét T [A] là một loại phụ của T [B]

+0

Vấn đề là, ngay cả khi bạn cũng định nghĩa 'ẩn đối tượng TB mở rộng T [B]', 'ngầm [T [B]] == T.TA'. http://lampsvn.epfl.ch/trac/scala/ticket/2509 – retronym

+0

có vẻ hợp lý, vì TA sau đó cụ thể hơn TB và TA là lao nên có thể sử dụng được ở đó T [B]. nếu bạn muốn ngầm xác định đúng loại, hãy xác định T là T [X] – IttayD

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