Return multiple types and halting the execution


#1
    private fun setupDatabase(): Database {

    Log.d(TAG, "Begin Couchbase Database")

    val manager: Manager

    try {
        manager = Manager(AndroidContext(this), Manager.DEFAULT_OPTIONS)
        Log.d(TAG, "Manager created")
    } catch (e: IOException) {
        Log.e(TAG, "Cannot create manager object")
        e.printStackTrace()
    }

    val dbname = "sog_couchbasedb"
    if (!Manager.isValidDatabaseName(dbname)) {
        Log.e(TAG, "Bad database name")
        return    // I want to stop execution here, but the kotlin suggest to make the return type Unit
    }

    val database: Database
    try {
        database = manager.getDatabase(dbname)
        Log.d(TAG, "Database created")

    } catch (e: CouchbaseLiteException) {
        Log.e(TAG, "Cannot get database")
        e.printStackTrace()
    }
    return database

}

I am having trouble deciding the return type of the above function. Kindly help!


#2

If you don’t want throw an exception then you can simply return a Database? and use return null.


#3

If I’d throw an exception. What exception shall I throw and how shall I use it for understanding what is happening?

ps. assume, I store the returned value in variable with type Database.


#4

How you handle the exception would depend on what you need the database for. If the database is an important part of your program and you can not execute without it, you might want to display an error message to the user and than exit. Or maybe you want to try to connect to a secondary database in case you can not connect to your main db.

On the other hand, if your database contains optional data, maybe it is fine for you program to execute without a db connected. In that case you would return and store your database in variable with type Database?

As to which exception to throw, this is often a hard question. I normally dislike to create a new Exception types for every case as it pollutes the codebase and is not really helpful, except if you want to later differentiate between to exceptions, but I normally stick to the default Java and Kotlin exceptions. In your case I would probably throw an IOException, because a database’s job is to ‘input’ and ‘output’ data (well yeah save data, same thing :wink: )

PS: I might be wrong there but in the part where you get catch part where you get the database also needs to throw an exception or return null (whatever you choose to do in case of an error :upside_down_face:)


#5

The simplest solution is:

require(Manager.isValidDatabaseName(dbname)) { "Bad database name $dbname" }

#6

Yeah, knowing and using the standard library functions require and check is really useful.
@thebali require is for checking the validity of passed arguments, and check for checking the state, e.g a variable in your class.

For the case “bad db name” I would also use require as @fvasco suggested even though it is technically not an argument to the function.


#7

I have written “return null” instead of “return”. Still no progress.

it says, null cannot be a value of non-null type Database

I want to stop the execution of all program and return some exception here. Also, I have another question, does throwing an exception means also halting the execution of program, or do we have to do it delibrately?


#8

The return value of your method is Database. Normally a variable or return value or any place where you have a type, you can not use null. If you want a type to be nullable you need to write a ? behind the type name to tell Kotlin that this is a nullable type. So your return type needs to be Database?.

You should take a look at https://kotlinlang.org/docs/reference/null-safety.html


#9

Thanks.

Also, it says, variable "manager" must be initialised.


#10

You only set it inside of a try block, which is may not execute, because it could fail. Therefor it is not always initialized, which leads to your error.


#11

How shall I correct the error, I meant?


#12
private fun setupDatabase(): Database? {

    Log.d(TAG, "Begin Couchbase Database")

    var manager: Manager? = null

    try {
        manager = Manager(AndroidContext(this), Manager.DEFAULT_OPTIONS)
        Log.d(TAG, "Manager created")
    } catch (e: IOException) {
        Log.e(TAG, "Cannot create manager object")
        e.printStackTrace()
    }

    val dbname = "sog_couchbasedb"
    if (!Manager.isValidDatabaseName(dbname)) {
        Log.e(TAG, "Bad database name")
        return null
    }

    var database: Database? = null
    try {
        database = manager!!.getDatabase(dbname)
        Log.d(TAG, "Database created")

    } catch (e: CouchbaseLiteException) {
        Log.e(TAG, "Cannot get database")
        e.printStackTrace()
    }
    return database

}

The function above is the final function I gathered from your knowledge. Please suggest any best practices I could use here which will result in efficient code.

Thank you.