Làm thế nào để từ khóa hợp nhất trong Kotlin hoạt động?


144

Tôi đang cố gắng để hiểu mục đích của reifiedtừ khóa, rõ ràng nó cho phép chúng tôi phản ánh về khái quát .

Tuy nhiên, khi tôi bỏ nó ra thì nó hoạt động tốt như vậy. Bất cứ ai quan tâm để giải thích khi điều này làm cho một sự khác biệt thực sự ?


Bạn có chắc bạn biết phản xạ là gì? Ngoài ra, bạn đã nghe nói về loại tẩy?
Mibac

3
Các tham số loại chung được xóa trong thời gian chạy, đọc về xóa loại nếu bạn chưa có. Các tham số loại được hợp nhất hóa trên các hàm nội tuyến không chỉ nội tuyến trong thân phương thức, mà cả tham số loại chung cho phép bạn thực hiện những việc như T :: class.java (mà bạn không thể làm với các loại chung thông thường). Đặt bình luận vì tôi không có thời gian để đưa ra câu trả lời đầy đủ ngay bây giờ ..
F. George

Nó cho phép có được quyền truy cập vào loại chung chung cụ thể của một hàm mà không cần dựa vào sự phản chiếu và không phải truyền loại đó làm đối số.
BladeCoder

Câu trả lời:


367

TL; DR: Điều gì reifiedtốt cho

fun <T> myGenericFun(c: Class<T>) 

Trong phần thân của một hàm chung như myGenericFun, bạn không thể truy cập loại Tvì nó chỉ khả dụng khi biên dịch nhưng bị xóa khi chạy. Do đó, nếu bạn muốn sử dụng kiểu chung như một lớp bình thường trong thân hàm, bạn cần phải vượt qua lớp một cách rõ ràng như một tham số như trong hình myGenericFun.

Nếu bạn tạo một inlinechức năng với một reified T tuy nhiên, loại Tcó thể được truy cập ngay cả trong thời gian chạy và do đó bạn không cần phải vượt qua Class<T>thêm. Bạn có thể làm việc với Tnó như thể đó là một lớp bình thường, ví dụ bạn có thể muốn kiểm tra xem một biến có phải là một thể hiện hay không T , điều mà bạn có thể dễ dàng thực hiện sau đó : myVar is T.

Thật là inline hàm có reifiedkiểu Tnhư sau:

inline fun <reified T> myGenericFun()

Cách reifiedlàm việc

Bạn chỉ có thể sử dụng reifiedkết hợp với một inlinechức năng . Hàm này làm cho trình biên dịch sao chép mã byte của hàm đến mọi nơi mà hàm đang được sử dụng (hàm đang được "nội tuyến"). Khi bạn gọi một hàm nội tuyến với kiểu hợp nhất, trình biên dịch sẽ biết loại thực tế được sử dụng làm đối số kiểu và sửa đổi mã byte được tạo để sử dụng trực tiếp lớp tương ứng. Do đó, các cuộc gọi như myVar is Ttrở thành myVar is String(nếu đối số kiểu là String) trong mã byte và tại thời gian chạy.


Thí dụ

Chúng ta hãy xem một ví dụ cho thấy mức độ hữu ích reifiedcó thể. Chúng tôi muốn tạo một chức năng mở rộng choString được gọi là toKotlinObjectcố gắng chuyển đổi một chuỗi JSON thành một đối tượng Kotlin đơn giản với một loại được chỉ định bởi loại chung của hàm T. Chúng ta có thể sử dụng com.fasterxml.jackson.module.kotlincho điều này và cách tiếp cận đầu tiên là như sau:

a) Cách tiếp cận đầu tiên mà không có loại thống nhất

fun <T> String.toKotlinObject(): T {
      val mapper = jacksonObjectMapper()
                                                        //does not compile!
      return mapper.readValue(this, T::class.java)
}

Các readValuephương pháp có một loại mà nó phải phân tích JsonObjectđể. Nếu chúng ta cố gắng để có được Classtham số loạiT , trình biên dịch sẽ phàn nàn: "Không thể sử dụng 'T' làm tham số loại hợp nhất. Thay vào đó, hãy sử dụng một lớp."

b) Giải pháp rõ ràng Class tham số

fun <T: Any> String.toKotlinObject(c: KClass<T>): T {
    val mapper = jacksonObjectMapper()
    return mapper.readValue(this, c.java)
}

Như một giải pháp thay thế, Classof Tcó thể được tạo thành một tham số phương thức, sau đó được sử dụng làm đối số choreadValue . Điều này hoạt động và là một mẫu phổ biến trong mã Java chung. Nó có thể được gọi như sau:

data class MyJsonType(val name: String)

val json = """{"name":"example"}"""
json.toKotlinObject(MyJsonType::class)

c) Cách thức của Kotlin: reified

Sử dụng một inlinehàm với reifiedtham số kiểu Tcho phép thực hiện hàm khác nhau:

inline fun <reified T: Any> String.toKotlinObject(): T {
    val mapper = jacksonObjectMapper()
    return mapper.readValue(this, T::class.java)
}

Không cần để có những Classsố Tthêm vào đó, Tcó thể được sử dụng như thể nó là một lớp học bình thường. Đối với khách hàng, mã trông như thế này:

json.toKotlinObject<MyJsonType>()

Lưu ý quan trọng: Làm việc với Java

Một hàm nội tuyến với reifiedkiểu không thể gọi được từJava .


5
Cảm ơn phản hồi toàn diện của bạn! Điều đó thực sự có ý nghĩa. Chỉ có một điều tôi đang tự hỏi, tại sao lại được thống nhất cần thiết nếu chức năng đang được nội tuyến? Nó sẽ để loại xóa và nội tuyến các chức năng nào? Điều này có vẻ hơi lãng phí đối với tôi, nếu bạn thực hiện chức năng, bạn có thể sử dụng loại nội dung đang được sử dụng hoặc tôi thấy có gì đó không đúng ở đây?
hl3mukkel

5
Cảm ơn phản hồi của bạn, thực sự tôi quên đề cập đến một điều có thể cho bạn câu trả lời: một hàm nội tuyến bình thường có thể được gọi từ Java nhưng một hàm có tham số loại thống nhất thì không thể! Tôi nghĩ rằng đây là một lý do tại sao không phải mọi tham số loại của hàm nội tuyến được tự động thống nhất.
s1m0nw1

Điều gì xảy ra nếu hàm là sự pha trộn của các tham số hợp nhất và không thống nhất? Điều đó làm cho nó không đủ điều kiện để được gọi từ Java bằng mọi cách, tại sao không tự động thống nhất tất cả các tham số loại? Tại sao kotlin cần phải được chỉ định rõ ràng cho tất cả các tham số loại rõ ràng?
Vairavan

1
Điều gì xảy ra nếu những người gọi cao hơn trong ngăn xếp không cần json.toKotlinObject <MyJsonType> (), nhưng json.toKotlinObject <T> () cho các đối tượng khác nhau?
bảy

1

ĐƠN GIẢN

* thống nhất là cho phép sử dụng tại thời điểm biên dịch (để truy cập T bên trong chức năng de)

ví dụ:

 inline fun <reified T:Any>  String.convertToObject(): T{

    val gson = Gson()

    return gson.fromJson(this,T::class.java)

}

sử dụng như:

val jsonStringResponse = "{"name":"bruno" , "age":"14" , "world":"mars"}"
val userObject = jsonStringResponse.convertToObject<User>()
  println(user.name)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.