hi, If I am not using covariance or contravariance...
# getting-started
g
hi, If I am not using covariance or contravariance, what's the difference between
Any
and
T
? I'm having difficulty wrapping my head around this.
h
Can you post a more concrete sample? For example, if you use List<Any> each element can be a different type, vs List<T> each element can be the same type (or Any, if there is no restriction and no other common super type).
g
Copy code
fun main() {
    val box = Box(1, "a")
    val box2 = Box2(4, "x")
}

class Box<T>(t1: T, t2: T)
class Box2(t1: Any, t2: Any)
Like in this case, I used T two times, but I could give different types of objects. So why would I use T instead of Any in this case?
h
In this concrete minified sample there is no real difference during runtime, because the upper type of
Int
and
String
is
Any
. But in practice, you rarely need unconstrained generic types but some upper type to access its members without casts.
g
But then when is there a difference difference? when I use two different objects whose upper type is not Any?
m
There are many differences, for example you can force T to be a child of a specific class or an interface, like the example below T needs to be
Closeable
and you can use
close
method from
Closeable
interface.
Copy code
interface Closeable {
    fun close()
}

class Box<T: Closeable>(t1: T, t2: T) {
    fun close() {
        t1.close()
        t2.close()
    }
}
g
Couldn't we just do this
class Box(val t1: Closeable,val t2:Closeable)
?
m
Well we could, but then you will loose smart casting, take a loot at the code below:
Copy code
interface Model {
    val id: String
}

data class User(
    override val id: String,
    val name: String,
    val email: String,
) : Model

data class State<T: Model>(
    val isLoading: Boolean = false,
    val isFailure: Boolean = false,
    val isSuccess: Boolean = false,
    val data: T,
) 

val state = State<User>(data = user)
val data = state.data // type User
data.name // We can access user properties without getting errors
Here
state.data
will be of type
User
, you can’t get this smart casting without generics.
Copy code
data class State(
    val isLoading: Boolean = false,
    val isFailure: Boolean = false,
    val isSuccess: Boolean = false,
    val data: Model,
)

val state = State(data = user)
val data = state.data // type Model not User -> can't access User properties
data.name // Unresolved reference: name
Same for Any.
🙌 1
e
@mohamed rejeb actually, smart casting means a different thing. This is just generic type, no cast going on, AFAIK.
👌 1
@Gamar Mustafa but Mohamed’s point is a good one. For example, suppose you have a class `ContainerT`:
Copy code
class Container<T>(private val value: T) {
    fun get(): T = value
}
Then you can use it like so:
Copy code
val container = Container("Some string")

// This type will be String, so you can call any String methods on it. If you had "Any", that wouldn't be possible (except with casting, but that can be unsafe)
val valueInContainer = container.get()
m
True no cast is going on, idk what to call haha, but let’s say that the ide is going to be smart here
g
@edrd valueInContainer2 is String here though:
Copy code
class Container2(val value: Any) {
    fun get(): Any = value
}
val container2 = Container("Some string")

val valueInContainer2 = container2.get()
h
During runtime, yes, but not during compile time:
valueInContainer2.length
does not compile
g
It does compile
m
It’s Containter not Containter2, that’s why
g
oh my bad, sorry)
okay, now I got it. Thanks @edrd, @mohamed rejeb, @hfhbd!!
🙌 1