Проблема в том, что вы даете обещание, которое компилятор не может доказать, что вы его сдержите.
Итак, вы создали это обещание: Вызов copy()
вернет свой собственный тип, полностью инициализированный.
Но тогда вы реализовали copy()
так:
func copy() -> Self {
return C()
}
Теперь я подкласс, который не отменяет copy()
. И я возвращаю C
не полностью инициализированный Self
(что обещал). Так что это нехорошо. Как насчет:
func copy() -> Self {
return Self()
}
Что ж, это не скомпилируется, но даже если бы это было так, это было бы бесполезно. У подкласса может не быть тривиального конструктора, поэтомуD()
может быть даже незаконным. (Хотя см. Ниже.)
Хорошо, а как насчет:
func copy() -> C {
return C()
}
Да, но это не возвращается Self
. Он возвращаетсяC
. Ты все еще не сдерживаешь своего обещания.
"Но ObjC может это сделать!" Ну вроде как. В основном потому, что его не волнует, сдержите ли вы свое обещание, как это делает Swift. Если вам не удастся реализовать copyWithZone:
в подклассе, вы можете не полностью инициализировать свой объект. Компилятор даже не предупредит вас, что вы это сделали.
«Но почти все в ObjC можно перевести на Swift, а в ObjC есть NSCopying
». Да, это так, и вот как это определяется:
func copy() -> AnyObject!
Таким образом, вы можете сделать то же самое (здесь нет причин!):
protocol Copyable {
func copy() -> AnyObject
}
Это говорит: «Я ничего не обещаю о том, что вы получите в ответ». Вы также можете сказать:
protocol Copyable {
func copy() -> Copyable
}
Это обещание, которое вы можете дать.
Но мы можем немного подумать о C ++ и вспомнить, что есть обещание, которое мы можем дать. Мы можем пообещать, что мы и все наши подклассы будем реализовывать определенные типы инициализаторов, и Swift будет обеспечивать это (и таким образом может доказать, что мы говорим правду):
protocol Copyable {
init(copy: Self)
}
class C : Copyable {
required init(copy: C) {
}
}
И вот как вы должны выполнять копии.
Мы можем сделать еще один шаг вперед, но он используется dynamicType
, и я не тестировал его тщательно, чтобы убедиться, что это всегда то, что мы хотим, но он должен быть правильным:
protocol Copyable {
func copy() -> Self
init(copy: Self)
}
class C : Copyable {
func copy() -> Self {
return self.dynamicType(copy: self)
}
required init(copy: C) {
}
}
Здесь мы обещаем, что есть инициализатор, который выполняет копии для нас, а затем мы можем во время выполнения определить, какой из них вызвать, предоставив нам синтаксис метода, который вы искали.