Vấn đề là bạn đang thực hiện một lời hứa mà trình biên dịch không thể chứng minh rằng bạn sẽ giữ.
Vì vậy, bạn đã tạo lời hứa này: Gọi copy()
sẽ trả về kiểu riêng của nó, được khởi tạo hoàn toàn.
Nhưng sau đó bạn thực hiện copy()
theo cách này:
func copy() -> Self {
return C()
}
Bây giờ tôi là một lớp con không ghi đè copy()
. Và tôi trả về một C
, không phải là một khởi tạo hoàn toàn Self
(mà tôi đã hứa). Vì vậy, đó là không tốt. Làm thế nào về:
func copy() -> Self {
return Self()
}
Chà, điều đó sẽ không biên dịch, nhưng ngay cả khi nó có, nó sẽ không tốt. Lớp con có thể không có hàm tạo tầm thường, vì vậy D()
thậm chí có thể không hợp pháp. (Mặc dù xem bên dưới.)
OK, vậy thì sao về:
func copy() -> C {
return C()
}
Có, nhưng điều đó không quay trở lại Self
. Nó trở lại C
. Bạn vẫn không giữ lời hứa của mình.
"Nhưng objC có thể làm được!" Chà, đại loại. Chủ yếu là vì nó không quan tâm nếu bạn giữ lời hứa của mình như Swift làm. Nếu bạn không thực hiện được copyWithZone:
trong lớp con, bạn có thể không khởi tạo hoàn toàn đối tượng của mình. Trình biên dịch thậm chí sẽ không cảnh báo bạn rằng bạn đã làm điều đó.
"Nhưng hầu hết mọi thứ trong ObjC đều có thể được dịch sang Swift, và ObjC thì có NSCopying
." Có, nó có, và đây là cách nó được định nghĩa:
func copy() -> AnyObject!
Vì vậy, bạn có thể làm tương tự (không có lý do gì cho! Ở đây):
protocol Copyable {
func copy() -> AnyObject
}
Điều đó nói rằng "Tôi không hứa hẹn bất cứ điều gì về những gì bạn nhận lại." Bạn cũng có thể nói:
protocol Copyable {
func copy() -> Copyable
}
Đó là một lời hứa bạn có thể thực hiện.
Nhưng chúng ta có thể nghĩ về C ++ một chút và nhớ rằng có một lời hứa mà chúng ta có thể thực hiện. Chúng tôi có thể hứa rằng chúng tôi và tất cả các lớp con của chúng tôi sẽ triển khai các loại trình khởi tạo cụ thể và Swift sẽ thực thi điều đó (và do đó có thể chứng minh rằng chúng tôi đang nói sự thật):
protocol Copyable {
init(copy: Self)
}
class C : Copyable {
required init(copy: C) {
}
}
Và đó là cách bạn nên thực hiện các bản sao.
Chúng ta có thể tiến thêm một bước nữa, nhưng nó vẫn sử dụng dynamicType
và tôi chưa thử nghiệm rộng rãi để đảm bảo rằng đó luôn là những gì chúng ta muốn, nhưng nó phải chính xác:
protocol Copyable {
func copy() -> Self
init(copy: Self)
}
class C : Copyable {
func copy() -> Self {
return self.dynamicType(copy: self)
}
required init(copy: C) {
}
}
Ở đây chúng tôi hứa rằng có một bộ khởi tạo thực hiện các bản sao cho chúng tôi và sau đó chúng tôi có thể xác định cái nào sẽ gọi trong thời gian chạy, cung cấp cho chúng tôi cú pháp phương thức mà bạn đang tìm kiếm.