2016-10-19 17 views
7

Tại sao Swift không cho phép tôi gán giá trị Foo<U> cho một biến loại Foo<T>, trong đó U là một phân lớp của T?Tại sao tôi không thể sử dụng một lớp con của một loại chung trong Swift?

Ví dụ:

class Cheese { 
    let smell: Int 
    let hardness: Int 
    let name: String 

    init(smell: Int, hardness: Int, name: String) { 
     self.smell = smell 
     self.hardness = hardness 
     self.name = name 
    } 

    func cut() { 
     print("Peeyoo!") 
    } 
} 

class Gouda: Cheese { 
    let aged: Bool 

    init(smell: Int, hardness: Int, name: String, aged: Bool) { 
     self.aged = aged 
     super.init(smell: smell, hardness: hardness, name: name) 
    } 

    override func cut() { 
     print("Smells delicious") 
    } 
} 

class Platter<Food> { 
    var food: Food 

    init(food: Food) { 
     self.food = food 
    } 
} 

let goudaCheese = Gouda(smell: 6, hardness: 5, name: "Gouda", aged: false) 
let goudaPlatter = Platter(food: goudaCheese) //Platter<Gouda> 

//error: cannot assign value of type 'Platter<Gouda>' to type 'Platter<Cheese>' 
let platter: Platter<Cheese> = goudaPlatter 

Nhưng tại sao không cho nó hoạt động? Bạn có thể gán cho một biến một đối tượng là một phân lớp của loại đó, ví dụ:

let gouda = Gouda(smell: 6, hardness: 5, name: "Gouda", aged: false) 
let cheese: Cheese = gouda 

Và bạn có thể thêm lớp con vào bộ sưu tập:

let plainCheese = Cheese(smell: 2, hardness: 5, name: "American") 
let gouda = Gouda(smell: 6, hardness: 5, name: "Gouda", aged: false) 
var cheeses: [Cheese] = [plainCheese] 
cheeses.append(gouda) 

Vậy làm thế nào là let platter: Platter<Cheese> = goudaPlatter khác nhau? Có bất kỳ hoàn cảnh nào không an toàn nếu nó hoạt động không? Nó chỉ đơn giản là một hạn chế của phiên bản hiện tại của Swift?

+0

Không tích cực, nhưng bạn có thể thay đổi 'Platter ' cho 'Platter ' (và sau đó 'var thực phẩm: T', 'init (thực phẩm: T)')? – Connor

+2

Generics là bất biến trong Swift - trong mắt của hệ thống kiểu, 'Platter ' và 'Platter ' hoàn toàn không liên quan. Nếu bạn muốn chuyển đổi giữa chúng, bạn cần viết mã để thực hiện chuyển đổi đó. 'Mảng' chỉ là một trường hợp đặc biệt mà trình biên dịch thực hiện một số phép thuật đằng sau hậu trường để thực hiện chuyển đổi này cho bạn (xem ví dụ [câu hỏi này] (http://stackoverflow.com/questions/37188580/why-isnt- somestruct-convertible-to-any)). – Hamish

+0

Related (có thể là dupe?): [Làm thế nào để lưu trữ một giá trị kiểu Class trong một từ điển kiểu \ [String: Class \] trong Swift?] (Http://stackoverflow.com/q/ 38590548/2976878) – Hamish

Trả lời

5

Bạn có thể giải quyết vấn đề này bằng kỹ thuật được gọi là type erasure. Về cơ bản, bạn tạo một cấu trúc "wrapper" để ẩn các chi tiết lớp cơ bản khỏi generic. Nó không phải lý tưởng, nhưng nó cho phép bạn thực hiện một cái gì đó tương tự như những gì bạn đang cố gắng làm.

class Cheese { 
    func doSomethingCheesy() { 
     print("I'm cheese") 
    } 
} 

class Gouda: Cheese { 
    override func doSomethingCheesy() { 
     print("I'm gouda") 
    } 
} 

struct AnyCheese { 
    let cheese: Cheese 
} 

class Container<T> { 
    init(object: T) { 
     self.object = object 
    } 
    let object: T 
} 

let cheese = Cheese() 
let cheeseContainer: Container<AnyCheese> = Container(object: AnyCheese(cheese: cheese)) 

let gouda = Gouda() 
let goudaContainer: Container<AnyCheese> = Container(object: AnyCheese(cheese: gouda)) 

cheeseContainer.object.cheese.doSomethingCheesy() // prints "I'm cheese" 
goudaContainer.object.cheese.doSomethingCheesy() // prints "I'm gouda" 
+0

Tôi đã bỏ phiếu cho câu trả lời của bạn, vì đó là giải thích tốt về xóa và tôi chắc chắn nó sẽ giúp những người tìm thấy câu hỏi này. Tuy nhiên, tôi đã không đánh dấu nó là chấp nhận bởi vì tôi đã yêu cầu giải thích tại sao Swift cư xử theo cách đó, thay vì tìm kiếm một công việc xung quanh. Tôi nghĩ rằng các ý kiến ​​về câu hỏi của tôi có thể được tái làm việc thành một câu trả lời tốt. – ConfusedByCode

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