Kotlin 1.3.11 has broken null-safety? - kotlin

fun handle() : String {
null?.let { return "Ololo"}
}
val result = handle()
result.trim() // kotlin.TypeCastException: null cannot be cast to non-null type kotlin.CharSequence
Any ideas why null-safe Kotlin function return null?

It's a bug caused by introducing contracts for the standard functions let, run, apply, also in Kotlin 1.3.
The fix is targeted to the version 1.3.20. See KT-28061 for details.

It looks like the Kotlin compiler is adding in a null return in case let doesn't execute. This is probably a bug, since it shouldn't compile, and doesn't in previous versions of Kotlin.
If we just compile your example, we get this:
#NotNull
public final String handle() {
return null;
}
I think that's just a compiler optimization, since null?.let() will never execute.
Using an actual variable yields:
#NotNull
public final String handle() {
return someNullableVariable != null ? "Ololo" : null;
}
In other words, let() doesn't execute if its reference is null. However, since this function needs to return something, the compiler just tells it to return null, since there's nothing else it could return.
Since the function is marked #NotNull, Kotlin will perform a null-check on anything referencing the function:
fun someOtherMethod() {
handle().trim()
}
Becomes
public final void someOtherMethod() {
String handle = handle();
if (handle != null) {
StringsKt__StringsKt.trim(handle).toString();
return;
}
throw new Exception("null cannot be cast to non-null type kotlin.CharSequence");
}
There are two ways to handle this. You could change the return type on handle() to String?:
fun handle(): String? {
someNullableVariable?.let { return "Ololo" }
}
Or you could return something else in case the variable is null:
fun handle(): String {
someNullableVariable?.let { return "Ololo" }
return "someNullableVariable was null"
}

It has to be a bug, because:
a return statement (or better: expression) is missing, since the lambda passed to let won't be invoked
a function with String as return type should never return null.
Interestingly, in Kotlin 1.2.x this does not even compile:
fun handle() : String {
null?.let { return "Ololo"}
}
Error:(6, 0) A 'return' expression required in a function with a block body ('{...}')
In Kotlin 1.3.11 it does.
In any case:
let won't be invoked, because the safe-call operator ? evaluates to null (in this case).

Related

Kotlin Contracts not working for null-check in extension function

I'm trying to write an extension function that returns true if the value is not null or 0 and use a contract to guarantee to the compiler that if I return true, the value is non-null. However, it doesn't seem to be working with smart casting. It still won't compile when I try to pass the value into a function that takes a non-nullable Long.
I tried to compile this code and it would not work. I expected the id to be smart-casted to a Long from a Long? since the contract guarantees that if isValidId returns true then the passed in Long? is not null.
As you can see, the property is immutable, so I don't think that's the issue. I also added some more code below, because the problem appears to be specific to extension functions. It works when I pass ID as a traditional parameter.
fun test() {
val id: Long? = null //5L
if (id.isValidID()) {
// It won't compile because the compiler says that id is a Long?
// instead of smart casting it to a Long. doWithId requires a Long.
doWithId(id)
}
}
fun doWithId(id: Long) {}
#OptIn(ExperimentalContracts::class)
fun Long?.isValidID(): Boolean {
contract { returns(true) implies (this#isValidID != null) }
return this != null && this != 0L
}
Thanks!
EDIT:
Oh! It works when it's not an extension function. Does anybody know how I can make extension functions work?
fun test() {
val id: Long? = null
if (isValidID(id)) {
// This compiles now that I pass ID as a param instead of
// passing it in an extension function.
doWithId(id)
}
}
fun doWithId(id: Long) {}
#OptIn(ExperimentalContracts::class)
fun isValidID(id: Long?): Boolean {
contract { returns(true) implies (id != null) }
return id != null && id != 0L
}

Kotlin custom Scope function return type not behaving as expected

this custom function call's a lambda block when null.
I expected the function definition below to enforce the same return type. : T
Is their a way to enforce that be block returns type T ?
inline fun <T> T?.whenNull(block: () -> T): T {
if (this == null) {
return block() //accepts any return type
} else {
return this
}
}
fun main() {
val x : Int? = null
println(x ?: 42)
println(x.whenNull { 42 })
println(x.whenNull { "why is kotlin not enforcing return of the same type?" })
}
T in the second whenAll call is being inferred as Any. Imagine that all occurrences of T are replaced Any, the call would be valid, wouldn't you agree? Int and String are both subtypes of Any, after all.
inline fun Any?.whenNull(block: () -> Any): Any {
if (this == null) {
return block()
} else {
return this
}
}
fun main() {
println(x.whenNull { "why is kotlin not enforcing return of the same type?" })
}
Basically, the Kotlin compiler is "trying too hard" here to make your code compile, and infers an unexpected type for your type parameter.
There exists an internal annotation #kotlin.internal.InputTypesOnly that would prevent your code from compiling if the type inferred is not mentioned in one of the input types (parameter types, receiver type, etc) of the function.
In this case, the input type is just Int?, and T is inferred to be Any, so it would be make your code not compile as expected. Unfortunately though, this annotation is internal, and you cannot use it :( KT-13198 is the ticket about making it public.
Interestingly, when passing this to the block the type is preserved and works as expected.
inline fun <T> T?.whenNullAlt(block: (T?) -> T): T {
if (this == null) {
return block(this) // "this" is superfluous, but enforces same return type
} else {
return this
}
}
fun main() {
val x : Int? = null
println(x.whenNullAlt { 42 })
println(x.whenNullAlt { "does not compile" })
}
As other answers pointed out, this is technically correct from Kotlin viewpoint because it can infer T to Any and it would compile.
However, while technically correct, this is a known problem in Kotlin language and is going to be fixed some day. That noted, let's try to understand why your answers's code works while your questions' doesn't.
The reason for this is that lambdas are always inferred last: imagine it as being a queue on what parts of the expression need to have their types inferred and anything inside a lambda is always at the end of the queue, no matter where the lambda is in the expression. So, when going over your example, it infers everything else, decides that the type of this should be Int?, than goes to the lambda, sees a String return type and merges them into Any being very proud of itself.
In the other example, however, the lambda is passed an external fact about it's parameter — the already inferred Int from the receiver (lambda is always the last to get the info, remember?). That way the inference inside the lambda fails because the argument and the result type are in disagreement.

kotlin: If value is null then exit, else use that value as not nullable

Essentially this is in the title. I have a value that could be null. If it is, I just want to exit with a message. If it's not null, then there's a whole slew of work I need to do with this value.
I've found similar, but not quite this exact situation. And it's the subtle difference that's driving me nuts. Here is my code in java:
if (value == null) {
print("error!");
return;
}
print(value);
doFunStuff(value);
etc(value);
All those methods using value require it to be non-null.
But I'm having a difficult time figuring this out in kotlin. With everything I try, the compiler still insists that value is still nullable and refuses to use it in the functions.
What is the kotlin way of doing this very common code flow?
If your methods truly have non-null parameters, the Kotlin compiler should be smart enough to do a smart cast to Object from Object?.
fun yourMethod(value: Object?) {
if (value == null) {
print("error!")
return
}
print(value) // Smart cast happens here
doFunStuff(value)
etc(value)
}
fun print(value: Object) {
// Implementation
}
fun doFunStuff(value: Object) {
// Implementation
}
fun etc(value: Object) {
// Implementation
}
But you can also force the conversion by using the !! operator (though in this case the compiler will tell you it's not necessary):
fun yourMethod(value: Object?) {
if (value == null) {
print("error!")
return
}
val nonNullValue = value!!
print(nonNullValue)
doFunStuff(nonNullValue)
etc(nonNullValue)
}
fun print(value: Object) {
// Implementation
}
fun doFunStuff(value: Object) {
// Implementation
}
fun etc(value: Object) {
// Implementation
}
If your value is a local variable or a function parameter, you won't have this problem, because the compiler will smart-cast it to not-null.
So, I'm assuming value in this case is a member property.
Option 1 is to copy it to a local variable to use in the function:
val value = value
if (value == null) {
print("error!")
return
}
print(value)
doFunStuff(value)
etc(value)
Option 2 is to use the let or also scope functions to do the same thing, but this might not be a good option here because so much code would become nested. This is more useful when you're only calling one or two functions with the object, in which case, you wouldn't even have to name it (just call it it).
value.let { value ->
if (value == null) {
print("error!")
return
}
print(value)
doFunStuff(value)
etc(value)
}
If your entire function works with this one property, you can avoid the nesting problem like this, if you don't mind it returning something besides Unit:
fun foo() = value.also { value ->
if (value == null) {
print("error!")
return
}
print(value)
doFunStuff(value)
etc(value)
}
Option 3 is to assert non-null every time you use it, but this is very ugly. This is only safe if you know the property is only ever accessed from the same thread this function is ever called on.
if (value == null) {
print("error!")
return
}
print(value!!)
doFunStuff(value!!)
etc(value!!)
Expanding on #Mehul's answer, this would only run the code in the let if the value was not null. If null, you could run the outside process and return from it.
value?.let { nonNullValue ->
print(nonNullValue);
doFunStuff(nonNullValue);
etc(nonNullValue);
}?: run { print("error!") ; return }
That said, since you are no longer needing the return to abort the function if null, you could simply do this and further clean it up replacing the lambda.
value?.let {
print(it);
doFunStuff(it);
etc(it);
}?: print("error!")
Well, have you already tried something like this and this is not what you expect?
value?.let { nonNullValue ->
print(nonNullValue);
doFunStuff(nonNullValue);
etc(nonNullValue);
}
basically the code inside let block will run only if the value isn't null.

Gson Type Adapter with both custom and default deserialization logic

I have a gson model class which contains a variable named duration which is an integer. The problem is that when this variable has no value the server instead of returning me null it returns "".
This is also the case with lists, when I am expecting a List<String>? and there is no value in them I get "" instead of null. Unfortunately the server can not change.
I have fixed the problem with a custom double deserializer but I need something more generic because the "" is the default server approach. Here is my progress so far but on the else branch I do not know how to tell it to continue with the default deserializer.
internal class BadDeserializer : JsonDeserializer<Any> {
#Throws(JsonParseException::class)
override fun deserialize(
json: JsonElement,
type: Type,
context: JsonDeserializationContext): Any? {
return try {
val json = json.asString
if (json.isBlank()) {
null
} else {
//Do nothing and call default behavior (?)
}
} catch (e: Exception) {
throw JsonParseException(e)
}
}
}
That's an unfortunate response getting an empty string.
AFAIK you can't register a type adapter for primitive types with GSON, though perhaps its possible to register one for their boxed types (not sure how this may work with Kotlin).
If you can use the boxed types, then you could use something like the following:
public class DoubleAdapter implements JsonDeserializer<Double> {
#Override
public Double deserialize(final JsonElement json,
final Type typeOfT,
final JsonDeserializationContext ctx) throws JsonParseException {
if (!json.isJsonPrimitive()) {
throw new JsonParseException("primitive expected");
}
final JsonPrimitive primitive = json.getAsJsonPrimitive();
if (primitive.isString()) {
final String value = primitive.getAsString();
if ("".equals(value)) return null;
throw new JsonParseException("double expected, not string");
}
if (!primitive.isNumber()) {
throw new JsonParseException("double expected");
}
return primitive.getAsNumber().doubleValue();
}
}
Though it might be cleaner (and I usually do for everything) to write an adapter for whatever structure you have.

Required <Object> and found <Object>?

class TaskRepo(taskData: TaskData) {
companion object {
private val repoByTask: LRUMap<String, OrderFormRepo> = LRUMap(2, 10);
fun getInstance(taskData: TaskData): OrderFormRepo {
if (notFoundObject(taskData.taskId)) {
repoByTask[taskData.taskId] = OrderFormRepo(taskData);
}
return repoByTask[taskData.taskId];//PROBLEM HERE
}
private fun notFoundObject(taskId: String): Boolean {
if (repoByTask.containsKey(taskId) && repoByTask[taskId] != null) {
return false
}
return true
}
}
}
in getInstance method of companion object I am getting compile time error:
Required TaskRepo and found TaskRepo?
LRUMap implements the Map interface, the get method of which in Kotlin returns a V?, as it returns null when no element is present for the given key.
As you've already done the checking beforehand in this case, you can be reasonably sure (assuming no other threads are modifying the map at the same time) that this value won't be null, and force a conversion to the non-nullable type with the !! operator:
return repoByTask[taskData.taskId]!!
For other ways to handle a missing key when reading from a Map, see the getOrDefault and getOrElse methods.