2016-04-12 22 views
9

EDIT: này hoạt động hoàn toàn tốt trong Swift 3, mà chúng ta nên tất cả được sử dụng bởi bây giờ :)Gõ Swift


Nếu tôi có hai giao thức, XY nơi Y thực hiện X, tại sao sao tôi không thể chỉ định mảng Y cho biến có loại [X]?

Thậm chí tò mò hơn, tôi có thể chuyển đổi từng cái một thành một mảng X và biên dịch tốt.

protocol X { } 

protocol Y: X { } 

// Make a test array of Y 
extension String: Y { } 
let a: [Y] = [ "Hello" ] 

// Make it into an array of X - this works absolutely fine 
let b: [X] = [ a[0] as X ] 

// Why won't this cast work? 
let c: [X] = a as [X] 

Tôi nghĩ rằng cho Y không tất cả mọi thứ X có thể làm, nhiệm vụ này nên được tốt (yea, tôi mất một số loại thông tin, nhưng nó ít nhất nên biên dịch!)

Bất kỳ ý tưởng?

EDIT: current answer chỉ ra rằng đó là điều nguy hiểm nếu bạn sử dụng mảng có thể thay đổi - mà tôi không nhận được nhưng bây giờ :) Tuy nhiên, nếu mảng của tôi là tất cả bất biến tại sao Swift sẽ không để điều này xảy ra? Ngay cả khi c là một mảng có thể thay đổi (tức là var c = a as [X]) tại sao Swift sẽ không sao chép nó, để lại a nguyên vẹn?

+0

Generics nhanh là bất biến. Không chắc chắn bạn có thể có generics covariant nhanh chóng. –

+0

Vấn đề với mã này là, bạn có thể thêm một 'Z' vào phân cấp cũng xuất phát từ X. Bây giờ Mảng của bạn có cả' Y' và 'Z', mặc dù' Z' không phải là một kiểu con của 'Y '. –

+0

@LukaJacobowitz: Tôi không nghĩ đây là vấn đề. Infact bạn vẫn có thể nối thêm các kiểu khác nhau phù hợp với 'Y'. Ví dụ. 'struct Z: X {}; b.append (Z())'. Bây giờ 'b' không chứa một phần tử của kiểu' String' và một phần tử của kiểu 'Z'. Và trình biên dịch là tốt với điều đó. –

Trả lời

7

Điều này không hiệu quả vì có thể gây ra một số sự cố. Ví dụ:

var squares: Array<Square> = Array<Square>() 
(squares as [Shape]).append(Circle()) 

Bây giờ chúng tôi có một vòng tròn trong mảng ô vuông. Chúng tôi không muốn điều đó, vì vậy trình biên dịch không cho chúng tôi biết. Trong các ngôn ngữ khác như Scala bạn có thể chỉ định Generics là covariant, contravariant hoặc invariant.

Nếu Swift cho phép bạn sử dụng covariant generics an Array<Square> sẽ là loại phụ của Array<Shape>.

Nếu sử dụng contravariance an Array<Square> sẽ là một siêu kiểu (!) Của Array<Shape>.

Nhưng khi sử dụng bất biến generics, không phải là loại phụ của cả hai.

Cách dễ nhất để làm điều đó trong trường hợp của bạn có lẽ sẽ là điều này:

let c = a.map{$0 as X} 

Bây giờ c là loại [X].

Để biết thêm thông tin về phương sai loại và lý do tại sao có thể gặp sự cố, bạn có thể truy cập this wiki page.

EDIT: Sau hơn nữa, có vẻ như vấn đề thực sự là giao thức cho phép triển khai mặc định và do đó có thể gây ra sự cố. Mã của bạn sẽ biên dịch hoàn hảo khi sử dụng các lớp. Dưới đây là một số mã có tiềm năng có thể dẫn đến các vấn đề với các giao thức:

protocol Shape { 
    func surfaceArea() -> Double 
} 

extension Shape { 
    func surfaceArea() -> Double { 
     return 0.0 
    } 
} 

protocol Circle : Shape { 
    func getRadius() -> Double 
} 

extension Circle { 
    func surfaceArea() -> Double { 
     return getRadius() * getRadius() * 3.14 
    } 
} 

Bây giờ, khi chúng tôi bị ném lên trời giữa hai giao thức này, phương pháp surfaceArea() trả về giá trị khác nhau, và Swift không cho phép nó.

Hãy thử điều tương tự với các lớp, thay vì giao thức và Swift sẽ không gặp bất kỳ sự cố nào khi biên dịch mã hiện tại của bạn. Đây là, đối với tôi, một quyết định kỳ lạ của nhóm Swift, nhưng tôi vẫn nghĩ cách tốt nhất để giảm thiểu là chỉ sử dụng

let c = a.map{$0 as X} 
+0

Khối mã đầu tiên của bạn không hoạt động vì một lý do khác. Lỗi trình biên dịch là 'lỗi: không thể sử dụng thành viên biến đổi trên giá trị bất biến của loại '[Hình dạng]''. Đây là ** NOT ** liên quan đến câu hỏi gốc. Sẽ giống với 'var list = [1,2,3]; (liệt kê là [Int]). Chắp thêm (4)' ở đó chúng ta sử dụng cùng một kiểu. Vui lòng đọc nhận xét của tôi bên dưới câu hỏi. –

+0

Giải thích hay. Trong trường hợp của tôi, tôi biết tôi an toàn với dàn diễn viên này, nhưng tôi có thể thấy lý do tại sao nó không được phép như vậy hey, tôi sẽ chỉ tìm một cách khác để làm những gì tôi đang cố gắng làm :) – deanWombourne

+0

@appzYourLife Câu hỏi của tôi đã hoàn toàn không có gì để làm với khả năng biến đổi và tất cả để làm với Generics. Vì vậy, nếu ví dụ mã không biên dịch thì _it ​​là khái niệm quan trọng_. – deanWombourne