Kiểu biến In và Out của Kotlin
Bài đăng này đã không được cập nhật trong 3 năm
Nếu bạn đã từng định nghĩa generic trong Kotlin, bạn sẽ để ý nhiều lần, nó sẽ đề xuất sử dụng từ khoá in
và out
để định nghĩa generic. Nó khiến chúng ta bắt đầu bối rối khi sử dụng, vậy tại sao?
Đây chính là cách để định nghĩa contravariance và covariant.
In và Out
Out (covariant type)
Nếu class generic của bạn chỉ sử dụng kiểu generic cho output của function, thì out
được sử dụng:
interface Production<out T> {
fun produce(): T
}
Chúng ta sẽ gọi nó là class/interface sản xuất (production), vì nó chủ yếu để sản xuất ra các kiểu generic. Do đó, rất đơn giản, có thể nhớ là:
produce = output = out.
In (contravariance type)
Nếu class generic chỉ sử dụng kiểu generic cho input của function, thì in
được sử dụng:
interface Consumer<in T> {
fun consume(item: T)
}
Chúng ta gọi nó là class/interface tiêu thụ (consumer), vì nó chủ yếu tiêu thụ các kiểu generic. Do đó, rất đơn giản, có thể nhớ là:
consume = input = in.
Invariant Type
Trong trường hợp class generic sử dụng kiểu generic như cả input và output cho function của nó, thì cả in
và out
sẽ không cần được sử dụng. Nó là invariant:
interface ProductionConsumer<T> {
fun produce(): T
fun consume(item: T)
}
Tại sao sử dụng in
và out
?
Bây giờ bạn đã có thể dễ dàng nhớ được khi nào sử dụng với in
và out
. Vậy ý nghĩa của chúng là gì? Trước khi tìm hiểu điều đó, chúng ta hãy xem ví dụ sau xác định đối tượng của class burger
. Nó là một fastFood
, một kiểu của food
. Sự phân cấp đơn giản như hình vẽ dưới đây:
open class Food
open class FastFood : Food()
class Burger : FastFood()
Burger Production
Quan sát interface generic Production
ở trên, hãy mở rộng chúng để sản xuất các đối tượng food
, fastfood
và burger
như bên dưới:
class FoodStore : Production<Food> {
override fun produce(): Food {
println("Produce food")
return Food()
}
}
class FastFoodStore : Production<FastFood> {
override fun produce(): FastFood {
println("Produce food")
return FastFood()
}
}
class InOutBurger : Production<Burger> {
override fun produce(): Burger {
println("Produce burger")
return Burger()
}
}
Bây giờ chúng ta có thể thực hiện như sau:
val production1 : Production<Food> = FoodStore()
val production2 : Production<Food> = FastFoodStore()
val production3 : Production<Food> = InOutBurger()
Chúng ta có thể thấy cả burger
hay fastFood
production, cũng đều là food
production. Vì vậy,
Đối với
out
generic, chúng ta có thể gán một class của subtype với một class của super-type
Nếu chúng ta thay đổi như dưới đây, nó sẽ xảy ra lỗi, bởi vì sản xuất food
và fastFood
không chỉ là burger
:
val production1 : Production<Burger> = FoodStore() // Error
val production2 : Production<Burger> = FastFoodStore() // Error
val production3 : Production<Burger> = InOutBurger()
Burger Consumer
Bây giờ, chúng ta hãy quan sát interface generic Consumer
ở trên, mở rộng nó với các consumer food
, fastfood
và burger
như sau:
class Everybody : Consumer<Food> {
override fun consume(item: Food) {
println("Eat food")
}
}
class ModernPeople : Consumer<FastFood> {
override fun consume(item: FastFood) {
println("Eat fast food")
}
}
class American : Consumer<Burger> {
override fun consume(item: Burger) {
println("Eat burger")
}
}
Chúng ta có thể assign chúng như sau:
val consumer1 : Consumer<Burger> = Everybody()
val consumer2 : Consumer<Burger> = ModernPeople()
val consumer3 : Consumer<Burger> = American()
Do vậy,
Đối với
in
generic, chúng ta có thể gán một class của super-type với một class của subtype
Nếu chúng ta thay đổi như dưới đây, sẽ xảy ra lỗi:
val consumer1 : Consumer<Food> = Everybody()
val consumer2 : Consumer<Food> = ModernPeople() // Error
val consumer3 : Consumer<Food> = American() // Error
Một cách khác để nhớ in
và out
SuperType có thể được gán cho SubType, sử dụng
in
SubType có thể được gán cho SuperType, sử dụngout
All rights reserved