Understanding Kotlin limitations for type parameter positions

Introduction

Kotlin variance modifiers áp đặt các giới hạn trên các loại tham số(Type Parameters) được sử dụng. Covariant type parameter(với out modifier) không thể được sử dụng ở những vị trí public, và contravariant type parameter(với in modifier) không thể được sử dụng ngoài những vị trí public. Nhưng tại sao lại giới thiệu những hạn chế như vậy?

Hãy tìm hiểu về nó.



Variance modifiers

Chúng tôi đã xuất bản một bài báo nhằm giải thích sâu về variance modifiers. Nó có thể được tìm thấy tại đây. Nó có thể được tổng kết ngắn gọn:

Khi một generic type là biến thể bất định, giống như class Box<T>, không có bất cứ mối quan hệ nào giữa Box<SomeType>Box<AnotherType>. Do đó không có mối quan hệ giữa Box<Number>Box<Int>

Khi một generic type là covariant, giống như class Box<out T>, khi A là một subtype của B thì Box<A> là một subtype của Box<B>. Do đó Box<Int> là một subtype của Box<Number>.

Khi một generic type là contravariant, giống như Box<int T>, khi A là một subtype của B thì Box<A> là một subtype của Box<A>. Do đó Box<Number> là một subtype của Box<Int>

Tổng kết ngắn:



Review limitations

Ngay cả khi Kotlin giới thiệu một số giới hạn cho type parameters bằng việc sử dụng variance modifiers. Lớp bên dưới là chính xác hoàn toàn.

lass SomeClass<T> {
    var t: T? = null

    fun functionReturningT(): T? = t

    fun functionAcceptingT(t: T) {}

    private fun privateFunctionReturningT(): T? = t

    private fun privateFunctionAcceptingT(t: T) {}
}

Ngay cả khi nó sẽ không được biên dịch nếu chúng ta giới thiệu bất cứ variance modifier:

class SomeClass<out T> {
    var t: T? = null // Error
    
    private var pt: T? = null

    fun functionReturningT(): T? = t

    fun functionAcceptingT(t: T) {} // Error

    private fun privateFunctionReturningT(): T? = t

    private fun privateFunctionAcceptingT(t: T) {}
}

class SomeClass<in T> {
    var t: T? = null // Error

    private var pt: T? = null

    fun functionReturningT(): T? = t // Error

    fun functionAcceptingT(t: T) {} 

    private fun privateFunctionReturningT(): T? = t

    private fun privateFunctionAcceptingT(t: T) {}
}

Như bạn có thể thấy, covariant không được sử dụng cho các phương thức public như là một loại parameter, và nó không thể được sử dụng cho các thuộc tính public dạng read/write. Chỉ có read là ổn bởi vì chúng chỉ lộ ra vị trí:

class SomeClass<out T> {
    val t: T? = null

    private var pt: T? = null

    fun functionReturningT(): T? = t

    private fun privateFunctionReturningT(): T? = t

    private fun privateFunctionAcceptingT(t: T) {}
}

Contravariance không thể được sử dụng như là một kiểu trả về từ các phương thức và áp dụng trên tất cả các phương thức(Tính rõ ràng của getter là tương đương với tính rõ ràng của thuộc tính).

Example problem

Để hiểu được vấn đề đằng sau các giới hạn này, hãy nghĩ đến Java arrays. Chúng là covariant và trong cùng thời gian, chúng cho phép thiết lập giá trị(theo vị trí). Như một kết quả, bạn có thể gọi dựa vào mã nguồn cái là hoàn toàn chính xác từ điểm biên dịch của view, nhưng sẽ luôn dẫn tới lỗi trong quá trình thực thi(runtime error):

// Java
Integer[] ints = { 1,2,3 };
Object[] objects = ints;
objects[2] = "AAA";


Điều gì đã xảy ra ở đây? Chúng ta ép kiểu ngược(up-casting - Ép kiểu thành đối tượng cha) mảng và rồi thiết lập loại ép kiểu xuôi(down-casting - Ép kiểu thành đối tượng con) và thế là boom! Chúng ta có một error. Nó liên quan tới vị trí như thế nào?

Positions and typing

In-position và out-position(Vị trí đầu vào và vị trí đầu ra) có một vài quy định ép kiểu chuẩn. Xem kiến trúc loại ở bên dưới.



Khi chúng ta cần lấy Dog cho vị trí đầu vào(in-position) - parameter cho phương thức takeDog(dog: Dog), mọi subtype cũng được chấp thuận.

fun takeDog(dog: Dog) {}takeDog(Dog())
takeDog(Puppy())
takeDog(Hund())

Khi chúng ta lấy Dog từ vị trí đầu ra, các giá trị được chấp nhận là Dog đồng thời cho tất cả các subtypes.



fun makeDog(): Dog = Dog()val any: Any = makeDog()
val animal: Animal = makeDog()
val wild: Wild = makeDog()

Chú ý rằng một khi yếu tố là in hay out position, các loại khác nhau của quá trình ép kiểu là mặc định, và nó không thể bị dừng.

Điều này xảy ra trong ví dụ về array của chúng ta. Covariance cho phép ép kiểu ngược(up-casting), và in-position cho phép ép kiểu xuôi(down-casting). Quá trình sử dụng hai kĩ thuật này đồng thời chúng ta có thể ép kiểu thành mọi thứ. Tương tự với Contravariance và out-position. Đồng thời cho phép người phát triển ép kiếu bất cứ loại nào thành bất cứ loại nào khác. Vấn đề chỉ là đó nếu một loại thực tế không thể bị ép theo cách thức này thì chúng ta có một runtime error.

Cách thức duy nhất để tránh điều này là cấm việc kết nối giữa các public in-position với contravariacne và public out-position với variance. Đây là lý do tại sao Kotlin có những giới hạn này. Kotlin cũng đã giải quyết vấn đề về array bằng cách tạo ra tất cả các arrays invariant. Nó là một ví dụ khác cái chứng tỏ Kotlin là một ngôn ngữ an toàn hơn Java(Xem thêm bài thuyết trình này).

Source

https://blog.kotlin-academy.com/understanding-kotlin-limitations-for-type-parameter-positions-15527b916034

Reference

https://blog.kotlin-academy.com/kotlin-generics-variance-modifiers-36b82c7caa39 https://blog.kotlin-academy.com/kotlin-next-level-of-android-development-95bce2f43a24 https://blog.kotlin-academy.com/programmer-dictionary-parameter-vs-argument-type-parameter-vs-type-argument-b965d2cc6929

P/S

Những bài đăng trên viblo của mình nếu có phần Source thì đây là một bài dịch từ chính nguồn được dẫn link tới bài gốc ở phần này. Đây là những bài viết mình chọn lọc + tìm kiếm + tổng hợp từ Google trong quá trình xử lý issues khi làm dự án thực tế + có ích và thú vị đối với bản thân mình. => Dịch lại như một bài viết để lục lọi lại khi cần thiết. Do đó khi đọc bài viết xin mọi người lưu ý:

1. Các bạn có thể di chuyển đến phần source để đọc bài gốc(extremely recommend).

2. Bài viết được dịch lại => Không thể tránh khỏi được việc hiểu sai, thiếu xót, nhầm lẫn do sự khác biệt về ngôn ngữ, ngữ cảnh cũng như sự hiểu biết của người dịch => Rất mong các bạn có thể để lại comments nhằm làm hoàn chỉnh vấn đề.

3. Bài dịch chỉ mang tính chất tham khảo + mang đúng ý nghĩa của một translated article được request từ phía cty mình.

4. Hy vọng bài viết có chút giúp ích cho các bạn(I hope so!). =)))))))