1

I am trying some android development with kotlin. In my case I want to overwrite: ContentProvider where I have to overwrite the function "query". "query" returns "Cursor" type. However, when I create the Cursor instance in the function with database.query I get back a "Cursor?" type. So I can only return Cursor if it is not null, but what do I do if it is null?

This is what it basically looks like:

override fun query(uri: Uri, projection: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, sortOrder: String?): Cursor {

    val cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder)
            // make sure that potential listeners are getting notified
            cursor?.setNotificationUri(getContext()?.getContentResolver(), uri)

            if(cursor != null)
                return cursor
            else
                // what to do here?

Any Ideas how to solve that?

Thanks, Sven

UPDATE First of all thanks for the answers.

Annotation seems not to work in my case as I can only access the compiled code and I dont get the option to annotate the source. Maybe I am missing something here.

Implementing my own cursor Seems like overkill especially if I have to do it every time that problem occurs.

So it seems my only option is to return cursor!! But I dont know how exactly to do it. My code is a bit more complicated than in my example, I missed a when statement. This is an updated version:

override fun query(uri: Uri, projection: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, sortOrder: String?): Cursor {

    val cursor = ??? //how to initialize cursor somehow?
    val db = database.getWritableDatabase()

    if(db != null) {
        val cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder)
                // make sure that potential listeners are getting notified
                cursor?.setNotificationUri(getContext()?.getContentResolver(), uri)

                if(cursor != null)
                    return cursor
     }

    // return what here? cursor does not exist
}

Do I really have to implement my own cursor and clutter my code with useless "throw UnsupportedExceptions"?

Lior Bar-On
  • 10,784
  • 5
  • 34
  • 46
sveri
  • 1,372
  • 1
  • 13
  • 28
  • Can `queryBuilder.query` ever return a `null` cursor? If not, I suggest [Using External Annotations](http://blog.jetbrains.com/kotlin/using-external-annotations/). – Franck Rasolo May 29 '14 at 23:09
  • There are new options available since this question was discussed, see the new answer below. – Jayson Minard Dec 29 '15 at 20:30

5 Answers5

1

Your options are:

  • If cursor is null, throw an exception: return cursor!! (this is OK in case you are sure that cursor is never actually null
  • If cursor is null, return some trivial cursor (e.g. your own implementation of AbstractCursor that behaves as an empty result set
  • (as Franck said in his comment) annotate QueryBuilder.query() function as @NotNull: http://blog.jetbrains.com/kotlin/using-external-annotations/
Andrey Breslav
  • 24,795
  • 10
  • 66
  • 61
1

There are libraries built around this topic:

The Result library for Kotlin gives a nice way to handle your case of "do this, or that" based on response values. And to have a real response and an exception or invalid response which is what you are seeking.

For Promises, you can find the same thing in the Kovenant library. With Kovenant promises can be resolved immediately and do not have to be async. So it works as a general purpose "I have a result, or not" library.

// ... in my function that returns Promise<Cursor, String>
if (good) {
   return Promise.ofSuccess<Cursor, String>(cursor)
}
else {
   return Promise.ofFail<Cursor, String>("no database connection")
}

Then in the caller of the function:

myFuncMakingACursor() success { cursor ->
    //called when all is ok, and I have a cursor
} fail {
    //called when promise is rejected
} always {
    //no matter what result we get, this is always called once.
}

Promises do not always have to have errors as the failure type. They can be anything you want. An error code, error message, data class, or an exception if you wish.

Both of these libraries give you manner for returning alternative results from a single function, and also for branching the code based on the results

These are good Kotlin alternatives to Optional and Maybe.

It is also fine to use nulls, and there are many operators that help to deal with null values in these cases. In Kotlin, what is the idiomatic way to deal with nullable values, referencing or converting them is another SO post covering nullability in general explaining some of the operators available (including the "safe call" operator used in some of the answers here).

Community
  • 1
  • 1
Jayson Minard
  • 84,842
  • 38
  • 184
  • 227
0
if (db != null){
    //...
} else {
    return null
}

and put return type to Cursor?

That is how i do usually. Don't know if it is bad or good.

lxknvlk
  • 2,744
  • 1
  • 27
  • 32
-1

may be you can update function end : Cursor to : Cursor? example:

override fun query(uri: Uri, projection: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, sortOrder: String?): Cursor? {}
CBBear
  • 1
  • 1
-3

There's a hack actually that allows you to trick the Kotlin compiler into accepting null as a non-nullable reference.

NullHack.java:

public class NullHack {
    private NullHack() {
    }

    @SuppressWarnings("ConstantConditions")
    @NotNull
    public static <T> T nullHack(@Nullable T object) {
        return object;
    }
}

NullHack.kt:

fun <T> T?.nullHack(): T = NullHack.nullHack(this)

Then in the ContentProvider subclass you can write the following code:

override fun query(uri: Uri, projection: Array<out String>?, selection: String?,
        selectionArgs: Array<out String>?, sortOrder: String?): Cursor {

    val db = database.getWritableDatabase()!!
    val cursor = queryBuilder.query(db, projection, selection, selectionArgs,
            null, null, sortOrder)
    // make sure that potential listeners are getting notified
    cursor?.setNotificationUri(getContext()?.getContentResolver(), uri)
    return cursor.nullHack()
}
Michael
  • 53,859
  • 22
  • 133
  • 139