Nullable upperbound


#1

Why this code isn't working now?

fun <T: Any?> get(key: String): T {
  if this.containsKey(key) return this.get(key)
  else return null // error cannot be null
}

Assume “this” is a Map.

  1. The funny thing is this.get(key) now returns nullable, but with fun <T: Any?> or fun <T> the compiler doesn’t complain about null ( return this.get(key) ). But when return null the compiler throws error.

  2. So, the only solution is to make the return as T?. And now everywhere in my code have to greatly refactored.

  3. The before I can use get<String> and get<String?>, now the expressiveness is gone.

  4. I don’t know anything about why people getting phobia about null.

  5. The only thing I now is that my code is getting slower because of I have to code if not null or if null everywhere, like everywhere literally.

6. Also now that any collection “get” function return nullable, which makes thousand lines of my codes have to be refactored. And again, if not null, if null, and so on.

7. I know how to make my program safe, but the compiler is freaking hell.


#2

Can you give some context as where this piece of code is at? You say `this` is already a `Map` in which case there is already a `get` method present.


#3

It's just an assumption, using extension method.

  1. This is just an example for people to directly test.
  2. This not about why don’t using another method already provided by kotlin library.

// This example is just a metaphor, return null is unnescesary if the upperbound is nullable, and I’m aware of that.
// This example I code it so that people can test it directly. It was meant to be used for JSON so that’s why I’m using Map as a metaphor.
// But anyway return null is now forbidden. The nullable upperbound has no meaning at all since kotlin M13.

@Suppress("UNCHECKED_CAST")
fun <T : Any?> Map<*, *>.get(key: Any?): T {
  if (this.containsKey(key)) return this.get(key) as T
  else return null
}

  1. this.get(key)  =====> now any .get is a nullable, but compiler won’t throw any compilation error, so funny because,
  2. return null ======> now throws compilation error because nullable upperbound is now changed. But what about the No. 3.
  3. Why is that .get which is nullable now, is not throwing any compilation error even if the generic signature changed to fun <T>, Why No. 3 now becoming error?

Note:

  • What I meant by saying any .get is now is a nullable is, the previous kotlin version M12 below, provided .get return as non-nullable T, now in M13 it’s changed to a nullable T.

  • Which is why now code written using .get or [] has to greatly refactored by people who’s using it. By means using if null or .? or if not null or .!!, like everywhere. Even if doesn’t even make sense to add it.


#4

I found a better solution that if I explicitly cast the null as T it will remove the compilation error.

I can only hope that that thing will not be forbidden again in the future.

I’m still faraway to refactor all my codes.


#5

To be able to return null from such method you need its return type to have nullable lower bound rather than upper. Otherwise consider you call this method with non-nullable type as T, say Int. How do you return null in this case?

One way to express such lower bound is to make return type to be T? (nullable supertype of T). This would cause warning about T being nullable itself, so you may want also to constraint T to be subtype of Any:

fun <T : Any> Map<*, *>.get(key: Any?): T? {
  if (this.containsKey(key)) return this.get(key) as T?
  else return null
}

#6

If that's the case then I don't even have to create this post anyway.

My preference is that I would try to avoid using nullable lowerbound in any case, and only using it when it is really nescessary.


#7

This code isn't working because it's incorrect: T can be anything at all, including, for example String, and null is not a String, so returning null would break the assumptions on the call site

> 1. The funny thing is this.get(key) now returns nullable, but with fun <T: Any?> or fun <T> the compiler doesn’t complain about null (return this.get(key) ). But when return null the compiler throws error.


This is because you are making a recursive call instead of calling Map.get. This code has both errors:

``

abstract class M : Map<String, String> {
  fun <T: Any?> get1(key: String): T {
  if (this.containsKey(key)) return this.get(key) // error
  else return null // error
  }
}


> 2. So, the only solution is to make the return as T?. And now everywhere in my code have to greatly refactored.


Sorry about it, we should have fixed this issue earlier on

#8

@Andrey

  1. My code isn’t like that at all. I’m not trying to inherit directly from Map. I already told above it’s using extension method. And if the error is about calling method recursively then I would be very stupid.

#9

So, in your code do you have a case where returning something nullable is not marked as a compiler error? If yes, please share some details, becuase it's a in the compiler bug that should be fixed