In this thread <https://kotlinlang.slack.com/archi...
# compiler
r
In this thread https://kotlinlang.slack.com/archives/C0B8MA7FA/p1641395257152000?thread_ts=1641388216.147600&amp;cid=C0B8MA7FA I was intrigued to discover that the compiler will already implicitly treat a type parameter
<T>
as
<T & Any>
in some circumstances if you also use it as
<T?>
.
y
This breaks if you make it
abstract class GenericMapper<out T>
(playground) I think it breaking shows that it's not necessarily treating the
<T>
in
nonNull
as
<T & Any>
, but more that it's actually declaring that no matter what T is, GenericMapper has to accept a nullable T, and of course
DisNonNullable
doesn't accept that. Think of it that because T is invariant,
DisNonNullable
could have a method that consumes T, and of course then it can't consume nulls, and so if it was allowed to call
nonNull
with its
GenericMapper<T?>
that can cause issues.
r
It does indeed, even if I declare
fun <T : Any> GenericMapper<T?>.nonNull()
, I presume because
T : Any
is a subtype of
T?
, so
GenericMapper<String>
is a subtype of
GenericMapper<String?>
, so the extension function applies.
y
Yep indeed. For that case, a viable solution is this:
Copy code
fun main(args: Array<String>) {
  DisNullable.nonNull()
  DisNonNullable.nonNull()
}

abstract class GenericMapper<out T>  {
  abstract fun map(string: String): T
}

fun <T> GenericMapper<T?>.nonNull(): GenericMapper<T> {
  val delegate = this
  return object : GenericMapper<T>() {
    override fun map(string: String): T {
      return delegate.map(string) ?: TODO("handle null")
    }
  }
}

@Deprecated(message = "Don't use this", ReplaceWith("this"), DeprecationLevel.ERROR)
@JvmName("non-null")
fun <T: Any> GenericMapper<T>.nonNull(): GenericMapper<T> = TODO()

object DisNullable : GenericMapper<String?>() {
  override fun map(string: String) = string
}

object DisNonNullable : GenericMapper<String>() {
  override fun map(string: String) = string
}
That's also a solution to the original, so yay freebie
👍 1