Smartcast es imposible porque propery tiene getter abierto o personalizado
Estoy aprendiendo Kotlin. Mi código es el siguiente:
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) decoupler.attachNotifier(this) if(activity is ScreenRouter) { decoupler.attachRouter(activity) } }
attachRouter()
:
- Error: Error: Los tipos enteros no están permitidos (en 'layout_height' con el valor '10')
- Qué son los tipos de datos en SQLite que soportan android
- Multiplicando un vector2 por un escalar (LibGDX)
- ¿Cómo saber cuál es el tipo MIME según el archivo?
- Android: Obtención de la lista de todos los archivos de un tipo específico
fun attachRouter(router: ScreenRouter?) { this.router = router }
Como se escribe en la documentación , kotlin automáticamente lanza al tipo después de comprobar con es operador. Por lo tanto, esperaba que funcionara. Pero en lugar de eso me molesta con el error de compilación diciendo:
Smartcast a ScreenRouter
es imposible porque la activity
es una propiedad que tiene getter abierto o personalizado.
Pensé que tal vez el error es porque la actividad puede ser anulable así que lo intenté:
if(activity!=null && activity is ScreenRouter) { decoupler.attachRouter(activity) }
Pero no funcionó y la compilación falló con el mismo error.
Sin embargo, el código siguiente funciona correctamente:
if(activity is ScreenRouter) { decoupler.attachRouter(activity as ScreenRouter) }
Su error bien pero por encima no parece explicar nada sobre por qué falla smartcast. No soy un experto de Kotlin, soy un principiante que aprende Kotlin. No encontré documentación en ninguna parte. Este tipo de descripciones de errores hace que Kotlin sea horrible de aprender. ¿Puede alguien explicar en términos simples?
- Las fuentes ttf personalizadas no se muestran correctamente en TextView en Android 4.4 KitKat
- Determinar el tipo de datos de una columna en SQLite
- Tabla de SQLite con columnas de columna de enteros
- Tipificación de un objeto de la clase padre al niño
- Kotlin - cuando la expresión con el tipo de retorno de la función
- Generic OR en lugar de AND <T extends Number | CharSequence>
- En Android OS o Java, ¿cuál es la diferencia entre una secuencia de caracteres y una cadena?
- Tipos Primitivos de Java: int vs. Integer
El punto clave aquí es que una propiedad open
o una propiedad con un getter personalizado no está garantizado para devolver el mismo valor en las llamadas sucesivas a ella.
Por lo tanto, el compilador no puede estar seguro de que, una vez que se ha comprobado el valor recibido de la propiedad, es seguro asumir que devolverá el mismo objeto o incluso un objeto del mismo tipo si se vuelve a llamar.
Ejemplo (bastante simplificado y sintético, sin embargo):
open class Base { open val value: List<Int> = ArrayList() } val b : Base = foo() fun printArrayList(list: ArrayList<Int>) { /* ... */ } if (b.value is ArrayList) { // first call printArrayList(b.value) // second call, smart cast is impossible }
Este código no se compilará, porque printArrayList()
espera una ArrayList
y b.value
está open
– eso es lo que obtienes en tu código. Ahora, hagamos una clase derivada que demuestre lo que podría salir mal:
class Derived : Base() { private var counter = 0 override val value: List<Int> get() { ++counter return if (counter % 2 == 0) ArrayList() else LinkedList() } } val b = Derived() println(b.value.javaClass) // class java.util.LinkedList println(b.value.javaClass) // class java.util.ArrayList
Aquí está bastante claro que si una propiedad está open
, puede ser anulada de manera que las sucesivas llamadas a ella devuelvan valores diferentes. En el ejemplo con printArrayList()
hay dos llamadas. Por eso el elenco inteligente no estaría a salvo. Lo mismo ocurre con las propiedades con getters personalizados.
Su ejemplo que realizó una operación as
-cast dentro del bloque if
funcionó porque el modelo fallaría y lanzaría una ClassCastException
si la propiedad devolvió un valor diferente de un tipo no compatible en la segunda llamada y esto preservaría la seguridad de tipo.
Y, por el contrario, si una propiedad val
no está open
y tiene un getter por defecto que simplemente devuelve el valor del campo de respaldo (que es final
en este caso), el compilador puede realizar con seguridad un cast inteligente: si obtiene el valor de La propiedad varias veces es seguro que será el mismo.
Una alternativa es obtener el valor una vez, almacenarlo en una variable local y utilizarlo varias veces en lugar de usar la propiedad de nuevo:
val list = b.value if (list is ArrayList) { printArrayList(list) // smart cast to ArrayList }
Ahora, no importa si una propiedad está open
, sólo hay una llamada a su getter, y el código entonces opera con el valor que devolvió la llamada. Dado que no puede cambiar, el elenco inteligente es posible aquí.