Я недавно наткнулся на случай, когда было бы очень удобно преобразовать экземпляр класса в подкласс, в то время как экземпляр был создан в родительском классе. Но я никогда такого не видел. Так есть ли способ сделать что-то вроде:Можно ли преобразовать экземпляр класса в экземпляр подкласса?
class Foo {
var name: String
}
class Bar: Foo {
var friendName: String
}
let foo = Foo(name: "Alice")
foo.toBar(friendName: "Bob")
// foo now of type Bar, as if I'd done
// foo = Bar(name: "Alice", friendName: "Bob")
Если это не возможно, есть несколько причин, это было бы невозможно с точки зрения дизайна?
=== редактировать === описание случая использования, где это может иметь смысл
Пусть говорит, что есть две точки зрения, представляющее то, что соответствует одной и той же записи базы данных для книги, на это только предварительный просмотр книга, а другая - более сложный вид. Модели могут быть:
protocol BookMetaDelegate {
func onReadStatusUpdate()
}
/// describe a book
class BookMeta {
var delegate: BookMetaDelegate?
private var _hasBeenRead: Bool
var hasBeenRead: Bool {
get {
return _hasBeenRead
}
set {
guard newValue != _hasBeenRead else { return }
_hasBeenRead = newValue
delegate?.onReadStatusUpdate()
}
}
var title: String
}
/// contains all the content of a book
class Book: BookMeta {
var content: BookContent
var lastPageRead: Int
/// some logic that only makes sense in a Book instance
func getLastPageRead() {
return content.getPage(lastPageRead)
}
}
и взгляды могут выглядеть следующим образом:
class BookPreview: UIView, BookMetaDelegate {
var book: BookMeta
init(book: BookMeta) {
book.delegate = self
}
func onReadStatusUpdate() {
print("read status has changed! UI should update")
}
}
class BookView: UIView {
var book: Book
init(book: Book) {
book.hasBeenRead = true
}
}
Тогда все может случиться, как
fetch(bookMetaWithId: 123).then { bookMeta in // bookMeta is of type BookMeta
let preview = BookPreview(book: bookMeta)
...
fetch(contentOf: bookMeta).then { content, lastPageRead in
bookMeta.asBook(content: content, lastPageRead: lastPageRead)
let bookView = BookView(book: bookMeta) // doing so will change the hasBeenRead flag and message the instance's delegate, ie the preview
...
}
}
Нет, это невозможно. Можно было бы создать * новый * 'Bar' из существующего' Foo', но вы не можете изменить тип существующего объекта, и для этого не было бы никакой пользы. –
Почему не было никакой пользы для этого? Это более точка зрения. Предположим, у меня есть два класса, которые представляют профиль пользователя и пользователя с большим количеством другой информации. Сначала я загружаю профиль, а позже получаю другую информацию. Тогда я мог бы отказаться от моего экземпляра UserProfile, используя дополнительные данные. – Guig
Вы используете «downcast» неправильно - вы опускаете фактический тип объекта, а не на другой. В вашем примере нет преимущества перед созданием нового объекта или создания «другой информации» (возможно) необязательного свойства в вашем исходном классе. Вот почему нет никакой пользы. Кажется, что «два класса, представляющие профиль пользователя и пользователя с большим количеством других сведений», являются реальной проблемой. Создание лучшего дизайна гораздо более разумно, чем полное разрушение системы типов. –