2014-12-05 18 views
5

Nếu ràng buộc loại Swift chung là tên giao thức, tôi có thể yêu cầu hai loại, bị ràng buộc với giao thức đó, phải là cùng loại. Ví dụ:cách nói "cùng một lớp" trong Swift generic

protocol Flier {} 
struct Bird : Flier {} 
struct Insect: Flier {} 
func flockTwoTogether<T:Flier>(f1:T, f2:T) {} 

Chức năng flockTwoTogether có thể được gọi với một chim và chim hoặc với một côn trùng và côn trùng, nhưng không phải với một Bird và côn trùng. Đó là giới hạn mà tôi muốn. Càng xa càng tốt.

Tuy nhiên, nếu tôi cố gắng điều tương tự với một tên lớp, nó không hoạt động:

class Dog {} 
class NoisyDog : Dog {} 
class WellBehavedDog: Dog {} 
func walkTwoTogether<T:Dog>(d1:T, d2:T) {} 

Vấn đề là tôi có thể gọi walkTwoTogether với một WellBehavedDog và NoisyDog. Đây là những gì tôi muốn ngăn chặn.

Có thực sự hai câu hỏi ở đây:

  • Có cách nào để nói rằng walkTwoTogether không thể được gọi với một WellBehavedDog và NoisyDog?

  • Đây có phải là lỗi không? Tôi hỏi vì nếu tôi không thể sử dụng chung để nói điều này, thật khó để hiểu tại sao nó hữu ích cho một ràng buộc chung là một tên lớp ở tất cả, vì chúng ta có thể nhận được kết quả tương tự chỉ với một hàm bình thường.

+0

Điều này có vẻ như hành vi mong đợi đối với tôi? Bạn đã hạn chế tùy chọn là loại Dog. cả hai lớp con của bạn đều phù hợp với loại đó và không có gì trong khai báo ràng buộc chúng. Nếu có một cách để làm những gì bạn muốn tôi mong đợi nó sẽ yêu cầu mệnh đề 'where', như trong' ', nhưng điều đó cũng ném lỗi. – cmyr

+0

@cmyr Vâng, tất nhiên tôi đã thử điều đó. Đó là lý do tại sao tôi hỏi nếu những gì tôi đang làm là không làm thế nào để làm điều đó, là nó có thể làm điều đó. :) – matt

+0

Tất cả đều hoạt động nếu 'Dog' là giao thức.Các ràng buộc chung không thể được thực thi trên các loại không giao thức. * (cái gì đó loại Erasure) * – mattt

Trả lời

3

Không phải là câu trả lời, cho mỗi gia nhập, nhưng một số dữ liệu hơn có lẽ ... Vấn đề là khi bạn gọi:

walkTwoTogether(NoisyDog(), WellBehavedDog()) 

Swift chỉ có thể đối xử với cả hai trường hợp, nếu như họ đang hợp của Dog (aka, upcast) - chúng ta cần điều đó để chúng ta có thể gọi các phương thức có nghĩa là đối với lớp A với các lớp con của A. (Tôi biết bạn biết điều này.)

Swift không upCast giao thức, vì vậy cách duy nhất để làm điều đó là để xác định một giao thức cho lớp con rằng cha không phù hợp với:

protocol Walkable {} 
extension NoisyDog : Walkable {} 
extension WellBehavedDog: Walkable {} 
func walkTwoTogether<T: Dog where T: Walkable>(d1:T, d2:T) { } 

walkTwoTogether(NoisyDog(), WellBehavedDog()) 
// error: type 'Dog' does not conform to protocol 'Walkable' 

các thông báo lỗi một cách rõ ràng cho thấy những gì đang xảy ra - cách duy nhất để gọi phiên bản này của walkToTogether là upCast các trường hợp phân lớp để Dog, nhưng Dog không phù hợp với Walkable.

+0

Thật may mắn, sau đó, tôi đã không trình bày vấn đề như tôi ban đầu đặt ra cho bản thân mình, đó là để phân biệt Dog từ NoisyDog! – matt

+0

Không có lớp con nào được phép! –

1

Tôi có thể nói rằng đây nên được coi là một lỗi từ inout thông số thay đổi các yêu cầu kiểu như họ nên được:

func walkTwoTogether<T:Dog>(inout d1:T, d2:T) { 

Bây giờ nó có hành vi dự kiến ​​mà bạn chỉ có thể vượt qua hai giá trị cùng loại . (Thử nghiệm trong Swift 1.2 và Swift 2 beta 5)

+0

Có và không. Điều này là hợp pháp: 'var d: Dog = WellBehavedDog(); walkTwoTogether (& d, NoisyDog()) 'Và trong mọi trường hợp, nếu có lỗi, tôi nghĩ rằng hầu hết chúng ta có thể nói rằng đó là sự mâu thuẫn; không rõ _which_ là đúng. – matt

+0

@matt Bạn nói đúng, nó có thể là một. Trong trường hợp của bạn, bạn phải đánh dấu cả hai tham số bằng 'inout'. Điều thú vị là nó hoạt động gần như giống nhau nếu bạn không sử dụng một ràng buộc chung. – Qbyte

+1

Tôi thấy khó hiểu hơn bao giờ hết! Nhưng datum của bạn là darned thú vị, không có nghi ngờ về điều đó. – matt

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