Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin RxJava Nullable Bug

Tags:

kotlin

rx-java

I've run into an issue in my Android app using Kotlin and RxJava. It's presented below.

import rx.Observable

data class TestUser(val name: String)

fun getTestUser(): Observable<TestUser> {
    return Observable.just(TestUser("Brian")).flatMap { getUser() } // this compiles
}

fun getTestUser2(): Observable<TestUser> {
    val observable = Observable.just(TestUser("Brian")).flatMap { getUser() }
    return observable // this does not compile
}

fun getUser(): Observable<TestUser?> {
    return Observable.just(null)
}

In getTestUser2, the compiler infers the final return type as Observable<TestUser?> and doesn't compile. However in getTestUser the code does compile, and when it's run, any subscriber to that observable may be in for a surprise when the TestUser comes back null.

I'm guessing it's something to do with going back and forth between Kotlin and Java. But, the fact that the compiler can see the difference in getTestUser2 makes me think this could be fixable.

Edit

This is on Kotlin 1.0, the final version released just yesterday (Feb 15, 2016).

like image 721
bclymer Avatar asked Feb 16 '16 19:02

bclymer


People also ask

Why can’t I use null in Kotlin?

Other issues caused by external Java code. In Kotlin, the type system distinguishes between references that can hold null (nullable references) and those that cannot (non-null references). For example, a regular variable of type String cannot hold null: To allow nulls, you can declare a variable as a nullable string by writing String?:

How do I unwrap an optional in Kotlin?

When mapping an Optional in Java, sometimes you have to unwrap another Optional. To do this, you use flatMap () instead of map (). With Kotlin’s null system, the value is either present, or null, so there’s nothing to unwrap.

What is the Kotlin equivalent of ofnullable?

The ofNullable () method works the same as of (), except that instead of throwing a NullPointerException, a null value produces an empty. The Kotlin equivalent is straightforward. The map () method allows you to transform an Optional to an Optional of another value. The equivalent in Kotlin is let ().

What is the type system in Kotlin?

Kotlin's type system is aimed at eliminating the danger of null references, also known as The Billion Dollar Mistake. One of the most common pitfalls in many programming languages, including Java, is that accessing a member of a null reference will result in a null reference exception.


1 Answers

The signature of the flatMap function is as follows when used in Kotlin:

public final fun <R: Any!, T: Any!> 
    Observable<T>.flatMap(
      func: ((T) -> Observable<out R!>!)!
    ) : Observable<R!>!

From the docs:

Any reference in Java may be null, which makes Kotlin’s requirements of strict null-safety impractical for objects coming from Java. Types of Java declarations are treated specially in Kotlin and called platform types. Null-checks are relaxed for such types, so that safety guarantees for them are the same as in Java

and

T! means “T or T?

This means that the Kotlin compiler can regard the return type of the flatMap function as either Observable<TestUser> or Observable<TestUser?>, or even Observable<TestUser>?. The relaxation part says so much as, "we don't want to bother you with these unknown types, you probably know better".

Since the return type is explicitly given in getTestUser(), it uses the first. Since the type of observable is not explicitly given, it infers it to Observable<TestUser?>, based on the getUser() function.


As @voddan commented, there is an open issue discussing this problem: https://youtrack.jetbrains.com/issue/KT-11108

like image 67
nhaarman Avatar answered Sep 17 '22 03:09

nhaarman