2015-08-22 16 views
5

Tôi đã đọc phần 20,7 của Lập trình cuốn sách trong Scala và tôi đã tự hỏi tại sao trong khi mã này biên dịch:loại Scala: Class A không phải là tương đương với T trong đó T là: loại T = A

class Food 
class Fish extends Food 
class Grass extends Food 

abstract class Animal { 
    type SuitableFood <: Food 
    def eat(food: SuitableFood) 
} 


class Cow extends Animal { 
    type SuitableFood = Grass 
    override def eat(food: Grass) {} 
} 


val bessy: Animal = new Cow 

bessy eat (new bessy.SuitableFood) 

mã này không (phần còn lại của mã là giống như trước đây, chỉ có những thay đổi dòng cuối cùng):

bessy eat (new Grass) 

Và như xa như tôi hiểu được loại cỏ là như nhau của Cow.SuitableFood.

Ngoài ra, tôi có một câu hỏi liên quan ví dụ này:

Nếu Bessy là loại động vật, làm thế nào có thể trình biên dịch biết rằng nó cần một loại SuitableFood -> Grass thay vì một loại thực phẩm? Vì cố gắng để cung cấp một thực phẩm mới mang lại cho tôi một lỗi biên dịch của loại không phù hợp, nhưng Animal lớp cần một loại thực phẩm và loại Bessy được định nghĩa một cách rõ ràng: Animal

+0

Đề xuất: thêm thẻ _path-dependent-type_ vào câu hỏi này. Điều đó có thể thu hút một câu trả lời từ một người biết nhiều hơn về chính xác loại khó khăn này. (Tôi vẫn đang đấu tranh với các loại phụ thuộc vào con đường mình.) –

+0

@BenKovitz Đã thêm, cảm ơn. – vicaba

Trả lời

10

Đó là vì bessie được khai báo Animal hơn Cow. bessie.SuitableFood là "loại phụ thuộc vào đường dẫn" (xem bên dưới).

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

val clarabelle: Cow = new Cow 

clarabelle eat (new Grass) 

này hoạt động bởi vì trình biên dịch có thể suy ra rằng clarabelle.SuitableFood = Grass từ clarabelle 's tuyên bố loại.

Kể từ bessie được khai báo Animal, không Cow, trình biên dịch có thể không an toàn suy luận rằng bessie.SuitableFood = Grass. * Khi bạn nói new bessie.SuitableFood, trình biên dịch tạo ra mã để xem xét các đối tượng thực tế bessie và tạo ra một thể hiện mới của loại thích hợp. bessie.SuitableFood là "loại phụ thuộc vào đường dẫn": "path" (phần bessie.) dẫn đến số nhận dạng cuối cùng (SuitableFood) thực sự là một phần của loại. Điều này cho phép bạn có một phiên bản tùy chỉnh của một loại cho từng đối tượng riêng lẻ của cùng một lớp.


* Vâng, thực sự, tôi nghĩ rằng nếu trình biên dịch là thông minh hơn một chút, nó có thể suy luận rằng bessie.SuitableFood = Grass, vì bessie là một val, không phải là một var, và do đó sẽ không thay đổi loại của nó. Nói cách khác, trình biên dịch phải biết rằng mặc dù bessie được khai báo là Animal, cô ấy thực sự là Cow. Có lẽ một phiên bản tương lai của trình biên dịch sẽ sử dụng kiến ​​thức này, và có lẽ có một lý do chính đáng tại sao đó sẽ không phải là một ý tưởng tốt, mà một người nào đó chuyên gia hơn tôi sẽ nói với chúng tôi. (Postscript: Một đã làm! Xem bình luận của Travis Brown dưới đây.)

+7

Về chú thích của bạn: nếu bạn đặt chú thích kiểu vào thứ gì đó, trình biên dịch sẽ xử lý nó như kiểu đó, ngay cả khi nó _could_ suy ra một cái gì đó cụ thể hơn. Bạn sẽ cần phải sử dụng một kiểu sàng lọc như 'val bessy: Animal {type SuitableFood = Grass}' nếu bạn muốn theo dõi thành viên kiểu chứ không phải kiểu con. –

1

Về phần thứ hai của câu hỏi của bạn: nó không. Animal không chỉ định rằng thực phẩm của nó là Food, nhưng một số loại phụ của Food. Trình biên dịch sẽ chấp nhận điều này, mã như ví dụ của bạn sẽ biên dịch, và sai như vậy. Trình biên dịch không biết rằng subtype cần thiết là Grass (đó là lý do tại sao eat(new Grass) không hoạt động), nó chỉ biết rằng có một số loại thực phẩm bò của bạn không thể ăn và thận trọng về nó.

1

Tôi tin rằng bessy eat (new bessy.SuitableFood) biên dịch là lỗi (được sửa trong 2.11).Vì một loại phụ khác là Animal có thể có một số SuitableFoodnew không có ý nghĩa, ví dụ: type SuitableFood = Food hoặc thậm chí type SuitableFood = Food with Int (Food with Int là một loại phụ hoàn toàn tốt đẹp của Food!).

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