Tengo la estructura Vector:
struct Vector{ var X : Measurement<Dimension> var Y : Measurement<Dimension> var Z : Measurement<Dimension> ...Y creo un nuevo objeto, por ejemplo:
let test = Measurement(value: 1.5, unit: UnitLength.inches) as Measurement<Dimension> var lVector = Vector(x: Measurement(value: 10, unit: UnitLength.meters), y: test, z: Measurement(value: 10, unit: UnitLength.meters))Todo funciona bien. Pero si trato de usar una variable de otra clase, aparece el error: "No se puede convertir el valor del tipo 'Medida' para escribir 'Medida' en coerción"
final class SettingsManager{ ... var test = Measurement(value: 1.5, unit: UnitLength.inches) ... } class Calculator { ... let test = SettingsManager.shared.test as Measurement<Dimension> var lVector = Vector(x: Measurement(value: 10, unit: UnitLength.meters), y: test, z: Measurement(value: 10, unit: UnitLength.meters))Probé "como!" y consiguió
Cast from 'Measurement<UnitLength>' to unrelated type 'Measurement<Dimension>' always failsLa misma variable declarada en esta clase funciona bien como mostré arriba. ¿Que hice mal?
Cuando declara la variable de test dentro de SettingsManager , no está mencionando explícitamente un tipo. Entonces, el algoritmo de inferencia de tipo de Swift decide que el tipo de variable depende del segundo parámetro en Measurement(...) .
En este caso, la test tiene el tipo de Measurement<UnitLength> . Si usó UnitArea.acres como segundo parámetro, su variable tendrá el tipo Measurement<UnitArea> . No Measurement<Dimension> .
Si menciona el tipo explícitamente como Measurement<Dimension> en primer lugar, resolverá el problema.
var test : Measurement<Dimension> = Measurement(value: 1.5, unit: UnitLength.inches) Ahora veamos por qué falla tu conversión. Puede convertir un tipo en su subtipo o supertipo. UnitLength es un subtipo de Dimension . Así que puedes lanzar entre ellos.
Pero Measurement<UnitLength> NO es un subtipo de Measurement<Dimension> . Por qué ?
La razón es simple. Los genéricos son invariantes. Esto significa que incluso si un tipo genérico envuelve un subtipo, no lo convierte en un subtipo de un genérico que envuelve su superclase.
Para obtener más aclaraciones sobre el párrafo anterior, lea esto .
Una Measurement<UnitLength> no es un tipo de Measurement<Dimension> . Estos son tipos no relacionados. Solo funciona aquí:
let test = Measurement(value: 1.5, unit: UnitLength.inches) as Measurement<Dimension> var lVector = Vector(x: Measurement(value: 10, unit: UnitLength.meters), y: test, z: Measurement(value: 10, unit: UnitLength.meters)) porque las llamadas de Measurement(...) aquí en realidad están creando Measurement<Dimension> s, ya que eso es lo que se espera en el sitio de llamada. as Measurement<Dimension> le indica que desea una Measurement<Dimension> , y el tipo de parámetro de x y z también son Measurement<Dimension> . El algoritmo de inferencia de tipos es lo suficientemente inteligente como para ver que también debe referirse a Measurement<Dimension>(...) .
Measurement<Dimension>.init toma una Dimension como su segundo parámetro, y UnitLength es un subtipo de eso, por lo que no hay problemas allí.
Por otro lado, en SettingsManager , declaraste test así:
var test = Measurement(value: 1.5, unit: UnitLength.inches) En ningún lugar aquí mencionaste Measurement<Dimension> , por lo que el algoritmo de inferencia de tipo solo usa el segundo parámetro para inferir que debes referirte a Measurement<UnitLength> , por lo que el tipo de test es Measurement<UnitLength> .
Si solo agrega as Measurement<Dimension>
var test = Measurement(value: 1.5, unit: UnitLength.inches) as Measurement<Dimension>deberia de funcionar.
Sin embargo, ¿no tendría más sentido que las tres componentes del vector tuvieran el mismo tipo de unidad?
struct Vector<T: Dimension>{ var X : Measurement<T> var Y : Measurement<T> var Z : Measurement<T> }Esto es solo para dejar de lado la pregunta de por qué esto es legal:
let test = Measurement(value: 1.5, unit: UnitLength.inches) as Measurement<Dimension> var lVector = Vector(x: Measurement(value: 10, unit: UnitLength.meters), y: test, z: Measurement(value: 10, unit: UnitLength.meters))Esto ni siquiera debería ser una pregunta; es sólo el principio de sustitución normal. Tomemos una forma más simple de expresarlo. ¿Qué pasa si no lanzas?
let test = Measurement(value: 1.5, unit: UnitLength.inches) Luego test se infiere como Measurement<UnitLength> . Ahora vamos a lanzar:
let test: Measurement<Dimension> = Measurement(value: 1.5, unit: UnitLength.inches) Eso es legal. ¿Por qué? No se debe a que Measurement<UnitLength> sea un subtipo de Measurement<Dimension> . ¡No lo es! Es porque UnitLength es un subtipo de Dimension. es como si hubieras dicho
let test = Measurement(value: 1.5, unit: UnitLength.inches as Dimension) ¿Lo ves? En realidad, no convertiste Measurement<UnitLength> en Measurement<Dimension> ; eso es imposible. Lanzas UnitLength hasta Dimension, lo cual es perfectamente posible. No es diferente a si hubieras dicho
let test2: Dimension = UnitLength.inches Siempre se le permite sustituir un subtipo donde se espera un supertipo. La dificultad en la pregunta original es que Measurement<UnitLength> no es un subtipo de Measurement<Dimension> (porque los genéricos de Swift no son automáticamente covariantes sobre su tipo parametrizado).