คีย์เวิร์ด reified ใน Kotlin ทำงานอย่างไร


165

ฉันพยายามทำความเข้าใจจุดประสงค์ของreifiedคำหลักดูเหมือนว่ามันช่วยให้เราสามารถไตร่ตรองเกี่ยวกับยาสามัญได้

อย่างไรก็ตามเมื่อฉันปล่อยมันออกไปมันก็ใช้งานได้ดี ใครสนใจที่จะอธิบายเมื่อสิ่งนี้สร้างความแตกต่างที่แท้จริง?


1
แน่ใจหรือว่าคุณรู้ว่าภาพสะท้อนคืออะไร? คุณเคยได้ยินเกี่ยวกับการลบประเภทหรือไม่?
Mibac

5
พารามิเตอร์ประเภททั่วไปจะถูกลบเมื่อรันไทม์อ่านเกี่ยวกับการลบประเภทหากคุณยังไม่ได้ดำเนินการ พารามิเตอร์ประเภท Reified ในฟังก์ชันอินไลน์ไม่เพียง แต่อินไลน์ในตัวเมธอดเท่านั้น แต่ยังรวมถึงพารามิเตอร์ประเภททั่วไปที่ช่วยให้คุณทำสิ่งต่างๆเช่น T :: class.java (ซึ่งคุณไม่สามารถทำได้กับประเภททั่วไปทั่วไป) ใส่เป็นความคิดเห็นเพราะฉันไม่มีเวลาหาคำตอบตอนนี้ ..
F. George

ช่วยให้สามารถเข้าถึงประเภทของฟังก์ชันทั่วไปที่เป็นรูปธรรมโดยไม่ต้องอาศัยการสะท้อนกลับและไม่ต้องผ่านประเภทเป็นอาร์กิวเมนต์
BladeCoder

@ เอฟจอร์จคุณมีเวลาหาคำตอบเต็ม ๆ ไหม?
IgorGanapolsky

คำตอบ:


406

TL; DR: อะไรreifiedดีสำหรับ

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

ในเนื้อความของฟังก์ชันทั่วไปเช่นmyGenericFunคุณไม่สามารถเข้าถึงประเภทได้Tเนื่องจากมีให้ใช้งานในเวลาคอมไพล์เท่านั้น แต่จะถูกลบเมื่อรันไทม์ ดังนั้นหากคุณต้องการใช้ประเภททั่วไปเป็นคลาสปกติในเนื้อหาของฟังก์ชันคุณต้องส่งผ่านคลาสเป็นพารามิเตอร์อย่างชัดเจนดังที่แสดงในmyGenericFun.

หากคุณสร้างinlineฟังก์ชันด้วยreified TประเภทของTสามารถเข้าถึงได้แม้ในขณะรันไทม์ดังนั้นคุณไม่จำเป็นต้องส่งผ่านClass<T>เพิ่มเติม คุณสามารถทำงานกับTราวกับว่ามันเป็นระดับปกติ - เช่นคุณอาจต้องการตรวจสอบว่าตัวแปรที่เป็นตัวอย่างของการ ที่คุณสามารถทำแล้ว:TmyVar is T

inlineฟังก์ชันที่มีreifiedประเภทดังกล่าวมีTลักษณะดังนี้:

inline fun <reified T> myGenericFun()

วิธีการreifiedทำงาน

คุณสามารถใช้reifiedร่วมกับinlineฟังก์ชันเท่านั้น คุณสั่งให้คอมไพเลอร์คัดลอก bytecode ของฟังก์ชันไปยังทุกจุดที่เรียกใช้ฟังก์ชันจาก (คอมไพเลอร์ "inlines" ของฟังก์ชัน) เมื่อคุณเรียกใช้inlineฟังก์ชันด้วยreifiedtype คอมไพลเลอร์จะต้องสามารถทราบชนิดจริงที่ส่งผ่านเป็นอาร์กิวเมนต์ type เพื่อให้สามารถแก้ไข bytecode ที่สร้างขึ้นเพื่อใช้คลาสที่เกี่ยวข้องได้โดยตรง ดังนั้นการเรียก like myVar is Tจึงกลายเป็นmyVar is Stringbytecode (ถ้าอาร์กิวเมนต์ type เป็นString)


ตัวอย่าง

ลองดูตัวอย่างที่แสดงให้เห็นว่ามีประโยชน์reifiedอย่างไร เราต้องการที่จะสร้างฟังก์ชั่นส่วนขยายสำหรับStringเรียกtoKotlinObjectว่าพยายามที่จะแปลงสตริง JSON ไปยังวัตถุที่ Kotlin Tธรรมดากับประเภทที่ระบุโดยประเภททั่วไปของฟังก์ชัน เราสามารถใช้com.fasterxml.jackson.module.kotlinสำหรับสิ่งนี้และแนวทางแรกมีดังต่อไปนี้:

ก) แนวทางแรกที่ไม่มีประเภท reified

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

readValueวิธีการเตะประเภทที่มันควรจะแยกJsonObjectไป หากเราพยายามรับClassพารามิเตอร์ type Tคอมไพเลอร์จะบ่นว่า: "ไม่สามารถใช้ 'T' เป็นพารามิเตอร์ reified type ได้โปรดใช้คลาสแทน"

b) วิธีแก้ปัญหาด้วยClassพารามิเตอร์ที่ชัดเจน

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

เป็นวิธีแก้ปัญหาที่Classของสามารถทำพารามิเตอร์วิธีการซึ่งจากนั้นจะนำมาใช้เป็นข้อโต้แย้งไปยังT readValueสิ่งนี้ใช้ได้ผลและเป็นรูปแบบทั่วไปในโค้ด Java ทั่วไป สามารถเรียกได้ดังนี้:

data class MyJsonType(val name: String)

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

c) วิธี Kotlin: reified

การใช้inlineฟังก์ชันที่มีreifiedพารามิเตอร์ type Tทำให้สามารถใช้ฟังก์ชันได้แตกต่างกัน:

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

ไม่จำเป็นต้องใช้ClassของTเพิ่มเติมTสามารถใช้งานได้ราวกับว่าเป็นคลาสธรรมดา สำหรับลูกค้ารหัสจะมีลักษณะดังนี้:

json.toKotlinObject<MyJsonType>()

หมายเหตุสำคัญ: การทำงานกับ Java

ฟังก์ชันอินไลน์ที่มีreifiedชนิดไม่สามารถเรียกใช้ได้จากโค้ดJava


7
ขอบคุณสำหรับคำตอบที่ครอบคลุม! นั่นเป็นเรื่องที่สมเหตุสมผล มีสิ่งหนึ่งที่ฉันสงสัยทำไมจึงจำเป็นต้อง reified ถ้าฟังก์ชันถูกอินไลน์อยู่ มันจะปล่อยให้ประเภทการลบและอินไลน์ฟังก์ชั่นต่อไปหรือไม่? สิ่งนี้ดูเหมือนจะเป็นการสิ้นเปลืองสำหรับฉันถ้าคุณอินไลน์ฟังก์ชั่นคุณอาจจะอินไลน์ประเภทที่ใช้อยู่หรือฉันเห็นอะไรผิดปกติที่นี่?
hl3mukkel

6
ขอบคุณสำหรับความคิดเห็นของคุณที่จริงฉันลืมพูดถึงบางสิ่งที่อาจให้คำตอบคุณ: ฟังก์ชันอินไลน์ปกติสามารถเรียกใช้จากJava ได้แต่ฟังก์ชันที่มีพารามิเตอร์ประเภท reified ไม่สามารถทำได้! ฉันคิดว่านี่เป็นเหตุผลว่าทำไมพารามิเตอร์ทุกประเภทของฟังก์ชันอินไลน์ไม่ได้รับการ reified โดยอัตโนมัติ
s1m0nw1

จะเกิดอะไรขึ้นถ้าฟังก์ชันเป็นการผสมผสานระหว่างพารามิเตอร์ reified และ non-reified? นั่นทำให้ไม่มีสิทธิ์ถูกเรียกจาก Java เลยทำไมไม่รีเฟรชพารามิเตอร์ประเภททั้งหมดโดยอัตโนมัติ เหตุใด kotlin จึงต้องระบุซ้ำสำหรับพารามิเตอร์ประเภททั้งหมดอย่างชัดเจน
Vairavan

1
จะเกิดอะไรขึ้นถ้าผู้เรียกระดับบนในสแตกไม่จำเป็นต้องเป็น json.toKotlinObject <MyJsonType> () แต่เป็น json.toKotlinObject <T> () สำหรับออบเจ็กต์ต่างกัน
7

3

ง่าย

* reified คือการให้สิทธิ์ในการใช้งานในเวลาคอมไพล์ (เพื่อเข้าถึง T inside de function)

เช่น:

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

    val gson = Gson()

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

}

ใช้เช่น:

val jsonStringResponse = "{"name":"bruno" , "age":"14" , "world":"mars"}"
val userObject = jsonStringResponse.convertToObject<User>()
  println(userObject.name)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.