MST

星途 面试题库

面试题:Kotlin中类型擦除在泛型场景下的体现

请阐述在Kotlin中,类型擦除是如何在泛型类和泛型函数中表现的?并举例说明类型擦除可能带来的问题及解决办法。
24.9万 热度难度
编程语言Kotlin

知识考点

AI 面试

面试题答案

一键面试

类型擦除在Kotlin泛型类和泛型函数中的表现

  1. 泛型类
    • 在Kotlin中,编译时泛型类型参数会被擦除,只保留其边界类型(如果有)。例如,对于泛型类class Box<T>(val value: T),编译后字节码中T会被擦除。如果没有为T指定上界,它会被擦除为Any?。假设定义Box<String>Box<Int>,编译后在字节码层面,它们本质上是同一个类,只是使用的方式在Java和Kotlin语法层面有区别。
    • 示例代码:
    class Box<T>(val value: T)
    fun main() {
        val stringBox = Box("Hello")
        val intBox = Box(10)
        // 这里stringBox和intBox编译后在字节码层面,Box类的结构是一样的,只是存储的值类型不同
    }
    
  2. 泛型函数
    • 泛型函数中的类型参数同样在编译时被擦除。例如fun <T> printValue(value: T),编译后T会被擦除。如果没有指定上界,也会被擦除为Any?。函数调用时,不同实际类型参数的调用,在字节码层面本质上也是同一个函数的不同调用方式。
    • 示例代码:
    fun <T> printValue(value: T) {
        println(value)
    }
    fun main() {
        printValue("World")
        printValue(20)
        // 这里两个printValue调用,编译后在字节码层面,函数主体是一样的,只是传入的参数类型不同
    }
    

类型擦除可能带来的问题及解决办法

  1. 问题:在运行时无法获取确切的泛型类型信息。例如,不能在运行时直接检查一个Box<String>实例的泛型类型是String
    • 示例代码:
    class Box<T>(val value: T)
    fun main() {
        val stringBox = Box("Test")
        // 以下代码不能直接获取stringBox的泛型类型是String
        // println(stringBox::class.typeParameters[0].type) // 这种写法会报错
    }
    
  2. 解决办法
    • 使用reified关键字(仅适用于内联函数)
      • 内联函数中的reified类型参数可以在运行时获取类型信息。例如:
      inline fun <reified T> printType() {
          println(T::class.simpleName)
      }
      fun main() {
          printType<String>()
          printType<Int>()
      }
      
    • 使用类型令牌(Type Token):通过传递一个表示类型的对象来获取类型信息。例如:
      class Box<T>(val value: T, val typeToken: KClass<T>) {
          fun printType() {
              println(typeToken.simpleName)
          }
      }
      fun main() {
          val stringBox = Box("Hello", String::class)
          stringBox.printType()
      }