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).
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?:
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.
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 ().
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.
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
orT?
”
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With