I’m curious, Is there a difference between: `class...
# announcements
s
I’m curious, Is there a difference between:
class Container<T>(val value: T)
and
class Container<T>(val value: T?)
? (note the extra
?
after the
T
)?
s
Yep - the difference is v subtle
so let’s say you pass in a
String?
to the first
Container
class’s constructor - you would get a
Container<String?>
back
if you passed in a
String?
to the second
Container
, you’d get back a
Container<String>
that just holds a nullable
String
IMO that second definition is kinda sketch, I would call it out in a code review. If you want a
Container<T>
that can potentially hold either
T
or
T?
, I’d recommend being explicit about it, like so:
Copy code
class Container<T : Any>(val value: T?)
💯 1
p
Using second declaration you can create instance using:
Copy code
Container<String>(null)
While this will not compile for first one.
s
The second one is suspect; i’m wondering if it has any valid usages…
For the seconds example `class Container<T>(val value: T?)`: What is the type of
value
in
Container<String?>
? What is its type in
Container<String>
? Is there such a type as
String??
🙂
p
String?
for both.
String??
is the same as
String?
.
s
@Shawn That indeed works this way if you let the compiler infer type:
Copy code
class Container1<out T>(val value: T)
class Container2<out T>(val value: T?)

fun main() {
    val s : String? = ""
    val x/*: Container1<String?>*/ = Container1(s)
    val y/*: Container2<String>*/ = Container2(s)

}
But you could still explicitly type
y
to a nullable one:
val y: Container2<String?> = Container2(s)
Is there any difference between the first
y
and the second
y
?
s
recalling from when a similar question was asked a while ago, I think the second
y
might break smartcasting
let me see if I can repro
adapted from the last time:
Copy code
class Wrapper<R>(private val instance: R? = null) {
    fun simpleName(): String {
        return if (instance != null) instance::class.simpleName ?: "default"
        else "default"
    }
}
it is admittedly a very contrived example, but this won’t compile
s
But this only won’t compile when using `::class`… and it won’t compile for
class Wrapper<R>(private val instance: R)
either…
s
you’re not wrong, and this is really a better argument for bounding
R
in this case I suppose
s
🙂 Basically,
class Container<T>(val value: T?)
is valid Kotlin, but, please, don’t use it 🙂
s
more or less lol
it might compile but I think you open yourself up to subtle type system-related bugs
maybe you could come up with a good kotlin puzzler for the next kotlinconf with this example 😛
p
I know this is quite stupid code, but nonetheless:
Copy code
class MyList<T>(
    val list: List<T>,
    val defaultValue: T? = null
) : List<T> by list {
    
    fun getOrDefault(index: Int): T?
    	= list.getOrElse(index) { defaultValue }
}
So you want to allow creation of both
MyList<String>
and
MyList<String?>
while
defaultValue
should always be nullable.
Pretty valid example where your “feature” can be useful 🙂
👍 1