Gunnar Bernstein
Gunnar Bernstein

Reputation: 6222

Room database query returns null where it should be null-safe in Kotlin

I have a query that does not have a result, when the DB is empty. Therefore NULL is the correct return value.

However, the compiler in Android Studio gives me the warning:
Condition 'maxDateTime != null' is always 'true'.

If I debug the code, the null check performs correctly as the value is actually null.

When I rewrite the interface to 'fun queryMaxServerDate(): String?' (notice the question mark), the compiler warning goes away.

But should not 'fun queryMaxServerDate(): String' result in a compilation error since it can be null?

@Dao
interface CourseDao {

    // Get latest downloaded entry
    @Query("SELECT MAX(${Constants.COL_SERVER_LAST_MODIFIED}) from course")
    fun queryMaxServerDate(): String

}

// calling function
/**
 * @return Highest server date in table in milliseconds or 1 on empty/error.
 */
fun queryMaxServerDateMS(): Long {
    val maxDateTime = courseDao.queryMaxServerDate()

    var timeMS: Long = 0
    if (maxDateTime != null) { // Warning: Condition 'maxDateTime != null' is always 'true'
        timeMS = TimeTools.parseDateToMillisOrZero_UTC(maxDateTime)
    }
    return if (timeMS <= 0) 1 else timeMS
}

Upvotes: 4

Views: 1895

Answers (1)

MikeT
MikeT

Reputation: 57083

The underlying code generated by the annotation is java and thus the exception to null safety as per :-

Kotlin's type system is aimed to eliminate NullPointerException's from our code. The only possible causes of NPE's may be:

An explicit call to throw NullPointerException(); Usage of the !! operator that is described below;

Some data inconsistency with regard to initialization, such as when:

  • An uninitialized this available in a constructor is passed and used somewhere ("leaking this");
  • A superclass constructor calls an open member whose implementation in the derived class uses uninitialized state;
  • Java interoperation:
    • Attempts to access a member on a null reference of a platform type;
    • Generic types used for Java interoperation with incorrect nullability, e.g. a piece of Java code might add null into a Kotlin MutableList, meaning that MutableList should be used for working with it;
    • Other issues caused by external Java code.

Null Safety

e.g. the generated code for queryMaxServerDate() in CourseDao would be along the lines of :-

  @Override
  public String queryMaxServerDate() {
    final String _sql = "SELECT max(last_mopdified) from course";
    final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
    __db.assertNotSuspendingTransaction();
    final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
    try {
      final String _result;
      if(_cursor.moveToFirst()) {
        final String _tmp;
        _tmp = _cursor.getString(0);
        _result = _tmp;
      } else {
        _result = null;
      }
      return _result;
    } finally {
      _cursor.close();
      _statement.release();
    }
  }

As you can see, no data extracted (no first row) and null is returned.

Upvotes: 2

Related Questions