티스토리 뷰

kotlin

[kotlin/코틀린] 리플렉션

hrniin 2023. 7. 19. 23:45
728x90

 

리플렉션

  • 클래스, 함수, 프로퍼티의 런타임 표현에 접근할 수 있게 해준다.

 

 

      1. KAnnotatedElement

annotation class Component(val name: String = "Core")

@Component("I/O")
class IO

fun main() {
    val component = IO::class.annotations.first() as Component
    println("Component name: ${component.name}") // Component name: Log
}
 
  • 가장 상위 리플렉션 타입으로, 모든 리플렉션 타입의 부모가 된다.
  • 함수, 프로퍼티, 클래스 등 요소에 정의된 어노테이션에 접근하는 기능을 제공한다.
  • 어노테이션 인스턴스의 리스트 프로퍼티(annotations) 하나만을 가지고 있다.
  • type alias에 붙은 어노테이션에 대한 리플렉션은 얻을 수 없다.

 

 

 

      2. KClassifier

 

        2-1. KClass

  • 컴파일 시점의 클래스, 인터페이스, 객체 선언을 런타임에 표현한다.
  • KClass 인스턴스를 얻는 방법
    • 클래스 리터럴 구문 사용
fun main() {
    println(String::class.isFinal)  // true
    println((1 + 2)::class)         // class kotlin.Int
}
 
    • kotlin 확장 프로퍼티 사용
fun main() {
    // Class -> KClass
    val stringClass = Class.forName("java.lang.String").kotlin
    println(stringClass.isInstance("Hello")) // true
     
    // KClass -> Class
    println(String::class.java) // class java.lang.String
}
 
      • java.lang.Class 인스턴스를 KClass 인스턴스로 변환
      • java 프로퍼티를 사용하면 반대로 변환 가능

 

  • 클래스 종류를 나타내는 프로퍼티들
 
val isAbstract: Boolean
val isCompanion: Boolean
val isData: Boolean
val isFinal: Boolean
val isInner: Boolean
val isOpen: Boolean
val isSealed: Boolean

 

  • 클래스의 접근 제한자를 나타내는 visibility 프로퍼티 (KVisibility 이넘 값을 가짐)
enum class KVisibility {
    PUBLIC,
    PROTECTED,
    INTERVAL,
    PRIVATE
}
private class A

fun main() {
    class B
     
    println(A::class.visibility) // PRIVATE
    println(B::class.visibility) // null
}
    • 코드 내에서 접근 제한자를 표현할 수 없다면 null이 된다. (ex: local class)

 

  • 클래스 이름을 나타내는 프로퍼티
val simpleName: String?
val qualifiedName: String?
fun main() {
   println(Any::class.simpleName) // Any
   println(Any::class.qualifiedName) // kotlin.Any
}
 
    • simpleName: 클래스의 간단한 이름을 반환한다.
    • qualifiedName: 클래스의 전체 경로가 들어간 이름을 반환한다.
    • local class 등의 경우 최상위 경로에서 접근할 방법이 없으므로 null이 반환된다.

 

  • isInstance()
fun main() {
    println(String::class.isInstance("")) // true
    println(String::class.isInstance(12)) // false
}
    • 인자가 현재 클래스의 인스턴스인지 알려준다.

 

  • 클래스 멤버 선언에 접근
val constructors: Collection<KFunction<T>>  // 주생성자, 부생성자 반환
val members: Collection<KCallable<*>>       // 멤버 함수, 프로퍼티 반환
val nestedClasses: Collection<KClass<*>>    // nested class와 객체 반환
val typeParametes: List<KTypeParameter>     // 타입 파라미터 반환
 
 
class Person(val firstName: String, val familyName: String)

fun main() {
    // Person KClass
    val personClass = Person::class
     
    // 주생성자를 KFunction 타입으로 받아서 call()을 통해 실행
    val person = personClass.constructors.first().call("John", "Doe") // Person 인스턴스
}

 

  • objectInstance: 객체에 대한 KClass에서 실제 객체 인스턴스를 반환
sealed class Color

data class Red(val r: Int, val g: Int, val b: Int) : Color()
data class Orange(val r: Int, val g: Int, val b: Int) : Color()
data class Yellow(val r: Int, val g: Int, val b: Int) : Color()

fun main() {
    val a = Color::class.sealedSubclasses
    println(a.joinToString { "$it" }) // class Orange, class Red, class Yellow
}
 
  • sealedSubclasses: selaed class인 경우 해당 클래스의 자식 클래스의 리스트를 반환 (List<KClass<out T>>)

 

open class A

open class B : A()

class C : B()

fun main() {
    println(C::class.supertypes) // [A, B] (x), [B] (o)
}
  • supertypes: 클래스가 직접 상속한 상위 타입만 반환. (List<KType>)

 

 

        2-2. KTypeParameter

  • 어떤 제네릭 선언의 타입 파라미터를 표현한다.

 

val isReified: Boolean
val name: String
val upperBounds: List<KType>
val variance: KVariance
  • upperBounds: 상위 바운드 타입이 담긴 리스트 반환

 

interface MyMap<K : Any, out V>

fun main() {
    val parameters = MyMap::class.typeParameters
    // K: [kotlin.Any], V: [kotlin.Any?]
    println(parameters.joinToString { "${it.name}: ${it.upperBounds}" })
}
 
    • 디폴트 바운드가 Any?이기 때문에 빈 리스트일 수 없다.

 

  • variance: KVariance 이넘 값으로 변성의 종류를 반환 (선언 지점 변성)
enum class KVariance {
    INVARIANT,
    IN,
    OUT
}

 

  • KType
val arguments: List<KTypeProjection>    // 타입 프로퍼티에 전달된 실제 타입 인자 목록
val classifier: KClassifier?            // 프로퍼티의 classifier (타입을 지정하는 클래스)
val isMarkedNullable: Boolean           // 널 가능성
 
fun main() {
    val type = typeOf<List<String>>() // KType
     
    println(type.arguments) // String
    println(type.classifier) // List
    println(type.isMarkedNullable) // false
}

 

 

 

      3. KCallable

  • 어떤 결과를 얻기 위해 호출할 수 있는 함수나 프로퍼티를 제공한다.
  • callable reference(::)를 사용해서 KCallable 인스턴스를 얻을 수 있다.
fun sum(a: Int, b: Int) = a + b
     
fun main() {
    println(::sum.returnType) // Int
}

 

  • 함수의 제한자를 알 수 있는 프로퍼티들
 
val isAbstract: Boolean
val isFinal: Boolean
val isOpen: Boolean
val isSuspend: Booelan
val visibility: KVisibility?

 

  • 프로퍼티나 함수의 시그니처를 표현하는 프로퍼티들
val name: String
val typeParameters: List<KTypeParameter>
val parameters: List<KParameter>
val returnType: KType
fun sum(a: Int, b: Int) = a + b
     
fun main() {
    println(::sum.name)             // sum
    println(::sum.typeParameters)   // []
    println(::sum.parameters)       // [Int, Int]
    println(::sum.returnType)       // Int
}
  • typeParameters: 함수의 제네릭 타입 파라미터

 

  • parameters
val simple = 1 // 멤버 x, 확장 x
val Int.ext get() = this // 멤버 x, 확장 o
class A { val Int.memberExt get() = this } // 멤버 o, 확장 o
     
fun main() {
    fun printParams(callable: KCallable<*>) {
        println(callable.parameters.joinToString { it.type.toString() })
    }
     
    printParams(::simple) // []
    printParams(Int::ext) // [Int]
    printParams(A::class.members.first()) // [A, Int]
}
    • 멤버 함수/프로퍼티거나 확장 함수/프로퍼티인 경우 첫 번째 원소는 receiver
    • 멤버인 동시에 확장인 경우 두 번째 원소도 receiver

 

  • call()
fun sum(a: Int, b: Int) = a + b
val i get() = 10
     
fun main() {
    println(::sum.call(5, 3)) // 8
    println(::i.call()) // 10
}
    • KCallable의 선언을 동적으로 호출할 수 있게 해주는 멤버 함수
    • 함수인 경우 함수를 호출, 프로퍼티인 경우 게터를 호출한다.

 

  • KParameter: 멤버/확장의 receiver, 함수/생성자의 파라미터에 대한 정보
val index: Int
val isOptional: Boolean
val isVararg: Boolean
val name: String?
val type: KType
val kind: KParamter
 
    • isOptional: 파라미터에 디폴트 값이 있는지 여부
    • kind
      • INSTANCE: 멤버 receiver
      • EXTENSION_RECEIVER: 확장 receiver
      • VALUE: 그 외 일반적인 파라미터

 

  • KProperty
val isConst: Boolean
val isLateinit: Boolean
 
var value = 1

fun main() {
    println(::value.getter()) // 1

    ::value.setter(2)
    println(value) // 2
}
    • getter()를 통해 프로퍼티 게터 접근
    • KMutableProperty의 경우 setter()를 통해 세터 호출

 

  • KFunction: 함수나 생성자를 표현
val isInfix: Boolean
val isInline: Boolean
val isOperator: Boolean
val isSuspend: Boolean
    • 함수의 종류를 알 수 있는 프로퍼티만 존재한다.

 

  • isAccessible
class A(private val secret: String)

fun main() {
    val a = A("Hello")
    val s = A::class.members.first() as KProperty1<A, String>

    s.isAccessible = true
    println(s.get(a)) // Hello
}
    • 이 프로퍼티를 통해 접근이 제한된 KCallable에 접근할 수 있다. 

 

728x90

'kotlin' 카테고리의 다른 글

[kotlin/코틀린] 위임 프로퍼티  (0) 2023.07.19
[kotlin/코틀린] 연산자 오버로딩  (0) 2023.07.19
[kotlin/코틀린] 어노테이션  (0) 2023.07.19
[kotlin/코틀린] 제네릭  (0) 2023.07.19
[kotlin/코틀린] 컬렉션  (0) 2023.07.19
250x250
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
글 보관함