'T' has a nullable upperbound


#1

I'm so confused with:

“Warning:(xx, xx) Kotlin: ‘T’ has a nullable upper bound. This means that a value of this type may be null. Using ‘T?’ is likely to mislead the reader”

But when I remove the ‘?’, I got:

“Condition ‘xxx == null’ is always false” or “Condition ‘xxx != null’ is always true”

I wanted to be able to check wheter is null or not.


#2

Could you please show a sample code with this issue? It's hard to guess what's going on without some code. E.g. in the following code there are no warnings:

 
fun <T> nullable(value: T) {
    if (value == null)
        println("null")
    else
        println("not null")
}

#3

As simple as your example, try to use that function.

suppress(“unchecked_cast”)
fun <T> nullable(): T {
  return “something” as T
}

val a = nullable<String>()
if (a == null) // will always false

Then,

suppress(“unchecked_cast”)
fun <T> nullable(): T? { // ‘T’ has a nullable upperbound
  return “something” as T
}

val a = nullable<String>()
if (a == null) // no more warning

Then,

suppress(“unchecked_cast”)
fun <T: Any?> nullable(): T {
  return “something” as T
}

// The same thing happen, except adding T?, but then again ‘T’ has a nullable upperbound
val a = nullable<String>()
if (a == null) // will always false


#4

Unless of course if I'm using like:

val a = nullable<String?>


#5

Ah, I see, so you want T to be non-nullable, but return nullable T?

Like this:

 
inline fun <reified T : Any> tryCast(value: Any): T? = value as? T

#6

I have this kind of code,

inline fun async(block: AsyncTask.() -> Unit) {
  AsyncTask().block()
}

open class AsyncTask {   open fun await<T : Any?>(future: Future<T>): T? {   return Fiber<T>(SuspendableCallable<T> { awaitInternal(future) }).start().get()   }   Suspendable   protected fun awaitInternal<T : Any?>(future: Future<T>): T? {   try {   return object : FiberAsync<T, Throwable>() {   override fun requestAsync() {           future.setHandler {            if (it.succeeded()) asyncCompleted(it.result())            else asyncFailed(it.cause())           }   }   }.run()   } catch (cause: Throwable) {   throw cause   }   } }

Without adding T?, I have to code explicitly like:

async {
  val data:JsonObject? = await(fetchData())

  // or
  val data = await<JsonObject?>(fetchData())
}

Although I know it’s the right way to do it.

So, basically I cannot infer it, unless if I put T? as the function return


#7

If I understand correctly what you want to get, you need to replace "Any?" constraint on T with "Any"

  open fun await<T : Any>(future: Future<T>): T?

But for me it infers type even as written, given the following declaration:

 

fun fetchData(): Future<String> {}

 
val data = await(fetchData())


It infers data as “String?”.

So, either I don’t understand the problem, or there is something else that causes unwanted effects.


#8

No, it will still the same.

I can’t do “if (data == null)” compiler will always complain that it will always return false, unless if I don’t do infer like “val data: JsonObject?


#9

That's weird, I don't see such a problem, it doesn't complain on null check. What version of Kotlin do you use?


#10

If I have to change from T: Any? to T: Any then when the return value is null, it will always be an NPE.

So I guess what can I understand right now is, from Kotlin perspective using T? is a bad practice.

Which means that I cannot use infer If I wanted the result to be nullable. I have to explicitly set the val type to nullable like :JsonObject?.


#11

I'm using the latest version of Kotlin 0.12.613 and IDEA 14.1.4 on Mac OS X 10.10.4


#12

Okay, let's simplify it to avoid extra dependencies (Quasar) and focus on type inference and semantics expected.

 
fun await<T>(future: Future<T>): T = future.get()
fun fetchData(): Future<String> {}
fun use() {
    val data = await(fetchData())
    if (data == null) {

  }
}


Here, it says that data is never null, rightfully, because fetchData should calculate String in the future, not nullable String. Correct?

If we change fetchData to return Future of nullable String, the null check is not marked with warning.

fun fetchData(): Future<String?> {}

Am I missing something?


#13

Yes, exactly.


#14

So, again, what is the problem? I'm sorry, seems I can't catch the issue here…


#15

The problem already gone as I already answerred before.

In conclusions,

  1. Dont’ use “T?
  2. If I wanted nullability then I cannot just infer. I have to explicitly declare it as nullable.

#16

Well, if it's solved for you, that's good. But I'm afraid there is something still not cleared.  

 
fun await<T>(future: Future<T>): T = future.get()
fun fetchData(): Future<String> {}

fun use() {
  val data: String? = await(fetchData())
  if (data == null) {

  }
}


By declaring data as String? you don’t do any good. Even if null check is not highlighted, it will never be true, at least without fooling type system in some other place.
It’s similar to this:

 
fun use() {
    val data: String? = ""
    if (data == null) {

  }
}


Though compiler doesn’t issue warning on null check, it’s in fact can never be null.


#17

That is way I declared the generic as <T: Any?>

By defining that:

  1. I don’t get NPE, if somebody trying to cast it.
  2. If I wanted the return to be not null, then I can just use infer. ( val data = await(fetchData() ). If it’s null it will still not throws NPE. But I cannot use if data == null.
  3. If I wanted to be nullable, then just declare it explicitly. (val data: JsonObject? = await(fetchData()). Then I can check for if data == null.

So, the compiler already did the great job. It was me that was wrong from the beginning.