2010-09-12 37 views
7

Vui lòng xem mã followin, trong đó Extractor[A,B] là một phần của khuôn khổ chung và mọi thứ khác nên được coi là "mã khách hàng" (tôi đã luộc nó xuống một chút và đổi tên Vì vậy, đừng bận tâm rằng Extractor dường như không quá hữu ích).Thừa kế và (tự động?) Loại chuyển đổi

scala> abstract class Extractor[A,B] {           
    | def extract(d:A):B              
    | def stringRepr(d:A):String            
    | }                   
defined class Extractor 

scala> sealed abstract class Value            
defined class Value 

scala> case class IntValue(i:Int) extends Value         
defined class IntValue 

scala> case class StringValue(s:String) extends Value       
defined class StringValue 

scala> case class Data(i:Int, s:String)           
defined class Data 

scala> sealed abstract class MyExtractor[Value] extends Extractor[Data, Value] { 
    | def stringRepr(d:Data) = extract(d) match {        
    |  case IntValue(i) => i.toString          
    |  case StringValue(s) => s            
    | }                  
    | }                   
defined class MyExtractor 

scala> class IntExtractor(name:String) extends MyExtractor[IntValue] { 
    | def extract(d:Data) = IntValue(d.i) 
    | } 
defined class IntExtractor 

scala> class StringExtractor(name:String) extends MyExtractor[StringValue] { 
    | def extract(d:Data) = StringValue(d.s) 
    | } 
defined class StringExtractor 

như vậy trong lời nói ngắn Extractor[A,B] được sử dụng để trích xuất một số giá trị B từ A và làm một số việc khác mà không phải là đại diện trong mã chương trình này. Các lớp trừu tượng ValueMyExtractor được sử dụng vì lý do loại savety trong "mã khách hàng". Khi tôi cố gắng tạo ra một List của MyExtractor s, sau đây sẽ xảy ra:

scala> val l = List.empty[MyExtractor[Value]] 
l: List[MyExtractor[Value]] = List() 

scala> new IntExtractor("test1") :: l 
res5: List[MyExtractor[_ >: IntValue <: Value]] = List([email protected]) 

cố gắng để chuyển đổi một IntExractor đến một lớp cha

scala> new IntExtractor("test"):MyExtractor[Value] 
<console>:24: error: type mismatch; 
found : IntExtractor 
required: MyExtractor[Value] 
     new IntExtractor("test"):MyExtractor[Value] 
    ^

scala> new IntExtractor("test"):Extractor[Data,Value] 
<console>:24: error: type mismatch; 
found : IntExtractor 
required: Extractor[Data,Value] 
     new IntExtractor("test"):Extractor[Data,Value] 

Tôi biết rằng mọi thứ đều tốt, khi tôi xác định IntExtractor như thế này

scala> class IntExtractor(name:String) extends MyExtractor[Value] { 
    | def extract(d:Data) = IntValue(d.i)        
    | } 
defined class IntExtractor 

scala> new IntExtractor("test"):Extractor[Data,Value]    
res17: Extractor[Data,Value] = [email protected] 

Nhưng tôi không hiểu, tại sao nó không hoạt động theo cách tôi thử nó ở trên. Tôi sẽ rất biết ơn vì bất kỳ trợ giúp hoặc gợi ý nào.

+6

Theo nguyên tắc chung, việc sử dụng tên của các lớp thực tế cho các tham số kiểu là rất hữu ích. Ví dụ: trong 'lớp MyExtractor [Value]' giá trị 'là một tham số kiểu và không liên quan gì đến' giá trị lớp 'của bạn. Việc sử dụng tên này có xu hướng gây nhầm lẫn cho con người, mặc dù tất nhiên nó không bao giờ làm fazes trình biên dịch. –

+0

'Extractor [A, B]' của bạn hoạt động giống như một 'A => B' (tức là một' Hàm1'). Lưu ý rằng 'Hàm1' là biến thể trong kiểu trả về của nó - nó được khai báo là' Hàm1 [-A, + B] ' –

+0

@ Randall Schulz: Tôi không phải là 100% shure Tôi hiểu bạn đúng nhưng' Giá trị' trong 'lớp MyExtractor [Value] 'DOES tương ứng với' class Value'. Ví dụ tôi chỉ kiểm tra các lớp con của 'Value' trong' stringRepr (d: Value): String' – Agl

Trả lời

7

Gần như tôi có thể nói, khái niệm bạn đang tìm kiếm là "hiệp phương sai". Chỉ vì IntValue là một loại phụ của Value không có nghĩa là MyExtractor[IntValue] là một loại phụ của MyExtractor[Value]. Theo mặc định, không có mối quan hệ kiểu con nào giữa hai loại đó. Để tạo một mối quan hệ như vậy, bạn cần khai báo MyExtractor để có thể so sánh với tham số của nó. Scala cho phép bạn khai báo các tham số kiểu là biến thể bằng cách thêm dấu "+" trước khai báo các tham số kiểu. Điều này được gọi là ký hiệu phương sai.

sealed abstract class MyExtractor[+Value] extends Extractor[Data, Value] {   
} 

Scala cũng hỗ trợ đối nghịch trên các thông số loại. Contravariance giống như hiệp phương sai, nhưng đảo ngược, và được thể hiện bằng ký hiệu "-" phương sai trên tham số kiểu. Loại Extractor của bạn cung cấp một ví dụ tuyệt vời về một nơi mà ký pháp đối xứng có ý nghĩa.

abstract class Extractor[-A,+B] {           
    def extract(d:A):B              
    def stringRepr(d:A):String            
}  

Điều này có nghĩa rằng nếu Foo là một subtype của Bar, sau đó Extractor[Bar, Baz] là một subtype của Extractor[Foo, Baz], mà nếu bạn nghĩ về nó làm cho tinh thần. Nếu một cái gì đó có thể trích xuất dữ liệu bạn muốn khi thông qua một thể hiện của một siêu kiểu, thì theo định nghĩa nó có thể trích xuất nó khi truyền một thể hiện của một kiểu con. Ngược lại, nếu Foo là loại phụ của Bar thì Extractor[Baz, Foo] là loại phụ của Extractor[Baz, Bar]. Điều đó cũng có ý nghĩa. Nếu bạn có một bộ giải nén trả về một số Foo, bạn có thể sử dụng nó bất cứ nơi nào bạn cần một bộ giải nén trả về một Bar.

Có những hạn chế về thời điểm đối nghịch và hiệp phương sai có thể được khai báo. Ví dụ, các tham số kiểu contravariant chỉ có thể được sử dụng như các đối số phương thức, và các tham số covariant chỉ có thể được sử dụng như là các phương thức trả về hoặc các vals. Không thể được sử dụng làm vars. Nó trở nên phức tạp hơn với các tham số kiểu lồng nhau, nhưng các quy tắc cơ bản đun sôi xuống thành "nơi nó hợp lý", và ví dụ của bạn đáp ứng tất cả chúng.

Lưu ý bổ sung, tất cả các lớp trừu tượng trong ví dụ của bạn có lẽ nên được khai báo là các đặc điểm thay thế. Miễn là các lớp trừu tượng của bạn không yêu cầu các đối số hàm tạo, hãy khai báo chúng như các đặc điểm cho bạn thêm một vài cơ hội để sử dụng lại.

+0

Giải thích rất tốt! – Landei

+0

wow! cảm ơn câu trả lời của bạn Dave. Để viết một câu chuyện dài: hãy thay đổi mọi thứ như bạn đã nói và nó hoạt động. Cảm ơn bạn! Để làm cho nó một chút lnger: bây giờ mà bạn chỉ ra nó thực sự rõ ràng (ngay cả những hạn chế) mặc dù tôi vẫn không thể có được đầu của tôi xung quanh điều đó contravariance. . . vẫn có một số suy nghĩ để làm;). Tôi đã sử dụng các lớp trừu tượng kể từ khi tôi không biết, các đặc điểm đó có thể có trình sửa đổi 'niêm phong'. Nhưng sau đó một lần nữa. . . tại sao không? ;) Cảm ơn rất nhiều – Agl

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