[java interop] How can I overcome java `@Nullable`...
# getting-started
d
[java interop] How can I overcome java
@Nullable
which is overly pessimistic? For example, I want to create a shortcut for transactions execution:
Copy code
fun <T> PlatformTransactionManager.execute(block: () -> T): T =
    TransactionTemplate(this).execute { _ -> block() }
This doesn't compile because
execute
is marked as
@Nullable
, therefore the return type must be
T?
. Instead would like it to be
T
, that is whatever
block
returns (which might also be null).
r
Can you just throw an exception?
Copy code
fun <T> PlatformTransactionManager.execute(block: () -> T): T =
    TransactionTemplate(this).execute { _ -> block() } ?: throw IllegalStateException("Expected it to be impossible for execute to return null")
d
As I said, the block can return null. I highlighted that part of my question 🙂
r
Oh I see, sorry
r
Copy code
fun <T> PlatformTransactionManager.execute(block: () -> T): T =
        TransactionTemplate(this).execute { _ -> block() } as T
this appears to be working, and it removes the warning
d
Yes, it works, but it generates a warning, not removes one 😞
r
oh weird, my IDE did not give any warning there...
image.png
e
if you don't want to see the warning on
as T
,
Copy code
@Suppress("UNCHECKED_CAST")
👍 1
r
hmm, weird that I don't need that suppression (did not disable that warning in IntelliJ)
d
Weird indeed. Does it compile without warnings? (btw I also use IDEA)
k
i feel stupid right now, but if it can return null how can it be
T
?
d
Plain
<T>
doesn't say anything about its nullability (in contrast to e.g.
<T : Any>
)
k
ah, thx
d
I guess the confusion might be about the difference between
T
and
T?
. Well,
T
in this case means the exact return type of the block (preserving nullability), while
T?
might not preserve the nullability, i.e. even if
block
returned non-null then
TransactionTemplate#execute
might still return null. I guess that's a principal deficiency of those java annotations.
r
Weird indeed. Does it compile without warnings? (btw I also use IDEA)
yes, no compiler warnings as well... I use Kotlin 1.8.10, btw
d
If you know that what you run in execute never returns a null, then why not use a
!!
?
Copy code
fun <T> PlatformTransactionManager.execute(block: () -> T): T =
    TransactionTemplate(this).execute { _ -> block() }!!
r
It can return null if the block returns null. It should have the same type as the block, which may or may not be nullable
d
As in, if
TransactionTemplate.execute
returns T? but you are always running
block: () -> T
then
TransactionTemplate(this).execute { _ -> block() }!!
should be OK?
r
i.e.
Copy code
val x: String = tm.execute { "not null" }
val y: String? = tm.execute { if (Random.nextInt(2) == 1) "not null" else null }
e
basically a mismatch between Java (where
<T>
is neither nullable nor non-nullable) and Kotlin (where there is such a distinction)
👍 1
Java
@Nullable <T> T foo()
becomes Kotlin
fun <T> foo(): T?
, Java
@NonNull <T> T foo()
becomes Kotlin
fun <T> foo(): T & Any
, and
<T> T foo()
can't be written directly in Kotlin due to platform nullability. none of them result in
fun <T> foo(): T
👍 1
d
I missed part of the original question as well "that is whatever
block
returns (which might also be null)." - this means that block will have to return
T?
so what I said was moot!
r
No, the block returns
T
. Whether or not `T`'s concrete type is nullable depends on how you define the block.
Copy code
val notNullBlock: () -> String = { "not null" }
val x: String = tm.execute(notNullBlock)

val nullableBlock: () -> String? = { if (Random.nextInt(2) == 1) "not null" else null }
val y: String? = tm.execute(nullableBlock)
d
yeah, sorry, would you change
PlatformTransactionManager.execute
to be
fun <T : Any>
for a non-nullable version?
r
Yes
👍 1
d
Thanks, sorry for my confusion there!
r
I missed the important bit too right at the start of the conversation