Kotlin Closable y SQLiteDatabase en Android

Uso este código en mi proyecto

fun getAllData(): List<Data> = writableDatabase.use { db -> db.query(...) } 

Se ejecuta correctamente en dispositivos Lollipop, pero en pre-Lollipop arroja una ClassCastException

 FATAL EXCEPTION: main java.lang.ClassCastException: android.database.sqlite.SQLiteDatabase cannot be cast to java.io.Closeable 

Probablemente porque es Java 1.6 y no tiene la function de probar con resources, pero no estoy seguro.

Entonces, ¿por qué tengo esta exception y cómo se puede arreglar?

El problema no tiene nada que ver con try-with-resources y es simplemente un problema de dependencia incompatible entre el código comstackdo y el entorno de time de ejecución.

El problema está en el momento de la compilation. SQLiteDatabase implementa la interfaz Closeable y luego la intercambia por otra implementación que no lo hace. Pero el código ya está comstackdo y no sabe acerca de este cambio.

Cuando llama a una function / método, se verifican todos los parameters para asegurarse de que sean los types correctos, esto generalmente lo maneja la JVM. Para las funciones en línea y de extensión, el comstackdor de Kotlin inserta una verificación de tipo para asegurarse de que está operando en el tipo correcto, y para esta function en línea específica, el receptor de la function se define como Closeable . Aquí está la firma del método:

 inline fun <T : Closeable, R> T.use(block: (T) -> R): R { ... } 

Entonces el comstackdor inserta una verificación de tipo en writableDatabase siguiente manera ( en bytecode ):

 CHECKCAST java/io/Closeable 

Entonces, en este punto, la implementación de SQLiteDatabase debe implementar para siempre la interfaz de SQLiteDatabase o el código comstackdo fallará. Al cambiar a una versión anterior de Android, donde esto ya no es cierto, se rompe el contrato y causa la exception. No hay forma de que el comstackdor pueda hacer algo diferente. Sería lo mismo que cambiar un JAR en cualquier aplicación JVM por uno con implementaciones radicalmente diferentes después de que ya haya comstackdo mi código. Cambiar las versiones de Android básicamente cambia todos los JAR.

Lo que puede hacer para evitar este problema es escribir su propia function de use específicamente para la class SQLiteDatabase si todas las versiones tienen un método de close ( copydo y modificado de la function de use Kotlin stdlib ):

 inline fun <T : SQLiteDatabase, R> T.use(block: (T) -> R): R { var closed = false try { return block(this) } catch (e: Exception) { closed = true try { close() } catch (closeException: Exception) { // eat the closeException as we are already throwing the original cause // and we don't want to mask the real exception } throw e } finally { if (!closed) { close() } } } 

Ahora la comprobación de tipo en línea será CHECKCAST android/database/sqlite/SQLiteDatabase que siempre tendrá éxito.

  • Causado por: android.database.sqlite.SQLiteException: cerca de "org": error de syntax (código 1):, al comstackr:
  • IlegalStateException donde se lanzó, al intentar recuperar los valores de la instancia de RoomDatabase
  • Agregar / Consultar / Analizar SQLite usando Anko
  • Implementación de SQLite en Kotlin y Anko
  • No se puede acceder a BaseColumns proporciona la propiedad _ID en Kotlin