'CellViewModel<ListItemTableViewCell>' to type 'CellViewModel<UITableViewCell>' in coercion
ListItemTableViewCell под капотом наследуется от UITableViewCell, почему не получается сконвертить одно в другое?
Очень интересный вопрос. Всё просто, в Swift пошли по пути наименьшего сопротивления, а это в свою очередь означает, что "все" дженерики находятся в инвариантной позиции. Несмотря на это, всё ж стандартные коллекции, скажем обычный массив, который имеет дженерик параметр Element, находится в ковариантой позиции. Именно поэтому 1ый пример нерабочий, а 2ой рабочий.
Вообще говоря возможность такой замены зависит от использования этого самого дженерик параметра. Если дженерик параметр используется только в качестве возвращаемого значения для методов и ридонли сабскриптов и свойств, то он находтся в ковариантной позиции и такая замена возможна: class B: A { } let foo0 = Foo<B> let foo1 = Foo<A> = foo0 Если дженерик параметр используется в качестве параметров у методов, то он находится в инвариантной позиции и замена выше уже невозможно, а такая замена возможна:: class B: A { } let foo0 = Foo<A> let foo1 = Foo<B> = foo0 Если дженерик параметр используется в обоих случаях, то единственный рабочий вариант в таком случае, когда A и B один и тот же тип. Это вообще говоря довольно сложная тема, поэтому в Swift все дженерик просто по умолчанию находятся в инвариантной позиции.
ну вроде логично, ведь это разные типы. то что там в конкретике используется nsview и nsbutton видимо не имеет значения, там ведь мог бы быть какой-нибудь double. если вариант ниже с массивами компилируется, то возможно это какими-нибудь расширениями и кастомными инициализаторами в массиве сделано
Это связано исключительно с понятиями ковариантности/контрвариантности Стандартные value типы на уровне компилятора поддерживают возможную ковариантность/контравариантность, тогда как пользовательские типы такой возможности не имеют, потому что за этим довольно сложно статически проследить, что бы это ничего не нарушило. То есть да, компилятору сложно проверить все места где используется этот параметр, что бы убедиться, что тут именно NSView —> NSButton, а не NSView —> Double. Но при этом, это работает в более простых случаях, не связанных с дженериками, скажем: let a = (NSView) -> NSButton = { ... } let b = (NSButton) -> NSView = a
Обсуждают сегодня