Kotlin을 사용한 반사

1. 소개

리플렉션은 런타임에 클래스, 필드 및 메서드를 검사,로드 및 상호 작용하는 기능의 이름입니다. 컴파일 타임에 그들이 무엇인지 모르는 경우에도 이것을 할 수 있습니다.

이것은 우리가 개발하는 것에 따라 많은 용도로 사용됩니다. 예를 들어 Spring과 같은 프레임 워크는이를 많이 사용합니다.

이에 대한 지원은 JVM에 내장되어 있으므로 모든 JVM 기반 언어에서 암시 적으로 사용할 수 있습니다. 그러나 일부 JVM 언어에는 이미 사용 가능한 것 외에 추가 지원이 있습니다.

2. 자바 리플렉션

모든 표준 Java Reflection 구조를 사용할 수 있으며 Kotlin 코드와 완벽하게 작동합니다 . 여기에는 java.lang.Class 클래스와 java.lang.reflect 패키지의 모든 것이 포함 됩니다.

어떤 이유로 든 표준 Java Reflection API를 사용하려는 경우 Java에서와 똑같은 방식으로 사용할 수 있습니다. 예를 들어 Kotlin 클래스의 모든 공개 메서드 목록을 얻으려면 다음을 수행합니다.

MyClass::class.java.methods

이것은 다음 구조로 나뉩니다.

  • MyClass :: classMyClass 클래스에 대한 Kotlin 클래스 표현을 제공합니다.
  • .javajava.lang.Class에 해당하는 값을 제공합니다.
  • .methodsjava.lang.Class.getMethods () 접근 자 메소드에 대한 호출입니다.

이것은 자바 또는 Kotlin에서 호출되는지, 그리고 자바 또는 Kotlin 클래스에서 호출되는지에 관계없이 정확히 동일하게 작동합니다 . 여기에는 데이터 클래스와 같은 Kotlin 특정 구성이 포함됩니다.

data class ExampleDataClass( val name: String, var enabled: Boolean) ExampleDataClass::class.java.methods.forEach(::println)

Kotlin은 반환 된 유형도 Kotlin 표현으로 변환합니다.

위에서 우리는 forEach ()를 호출 할 수 있는 kotlin.Array 를 얻습니다 .

3. Kotlin 반사 향상

표준 Java Reflection API를 사용할 수 있지만 Kotlin이 플랫폼에 제공하는 모든 확장을 인식하지는 않습니다 .

또한 경우에 따라 일부 상황에서 사용하는 것이 약간 어색 할 수 있습니다. Kotlin은 이러한 문제를 해결하는 데 사용할 수있는 자체 리플렉션 API를 제공합니다.

Kotlin Reflection API의 모든 진입 점은 참조를 사용합니다. 앞서 클래스 정의에 대한 참조를 제공하기 위해 :: class 를 사용하는 것을 보았습니다 . 또한이를 사용하여 메서드와 속성에 대한 참조를 얻을 수 있습니다.

3.1. Kotlin 클래스 참조

Kotlin Reflection API를 사용하면 클래스 참조에 액세스 할 수 있습니다. 그런 다음 Kotlin 클래스의 전체 세부 사항을 조사하는 데 사용할 수 있습니다 . 이를 통해 자바 클래스 참조 ( java.lang.Class 객체)뿐만 아니라 모든 Kotlin 관련 세부 정보에 액세스 할 수 있습니다.

클래스 세부 정보를위한 Kotlin API는 kotlin.reflect.KClass 클래스를 중심으로합니다 . 이것은 임의의 클래스 이름 또는 인스턴스에서 :: 연산자를 사용하여 액세스 할 수 있습니다 ( 예 : String :: class).

또는 Java Class 인스턴스를 사용할 수 있는 경우 확장 메소드 java.lang.Class.kotlin 을 사용하여 액세스 할 수 있습니다 .

val listClass: KClass = List::class val name = "Baeldung" val stringClass: KClass = name::class val someClass: Class val kotlinClass: KClass = someClass.kotlin

우리가 얻은 후에 KClass의 오브젝트를,이 문제의 클래스에 대한 정보를 알 수있는 몇 가지 간단한 일이있다 . 이들 중 일부는 표준 Java 개념이고 다른 일부는 Kotlin 특정 개념입니다.

예를 들어, Class가 Abstract인지 Final인지 쉽게 알 수 있지만 Class가 Data Class인지 Companion Class 인지도 알아낼 수 있습니다.

val stringClass = String::class assertEquals("kotlin.String", stringClass.qualifiedName) assertFalse(stringClass.isData) assertFalse(stringClass.isCompanion) assertFalse(stringClass.isAbstract) assertTrue(stringClass.isFinal) assertFalse(stringClass.isSealed)

또한 클래스 계층 구조를 이동할 수있는 방법도 있습니다. Java에서 우리는 이미 Class에서 그것의 수퍼 클래스, 인터페이스 및 그것이 포함 된 외부 클래스로 이동할 수 있습니다.

Kotlin은 여기에 임의의 클래스에 대한 Companion Object와 Object 클래스에 대한 Object 인스턴스 를 얻는 기능을 추가합니다 .

println(TestWithCompanion::class.companionObject) println(TestWithCompanion::class.companionObjectInstance) println(TestObject::class.objectInstance)

Java에서와 거의 같은 방식으로 Class Reference에서 클래스의 새 인스턴스를 만들 수도 있습니다 .

val listClass = ArrayList::class val list = listClass.createInstance() assertTrue(list is ArrayList)

또는 생성자에 액세스하고 필요한 경우 명시 적 생성자를 사용할 수 있습니다. 다음 섹션에서 설명하는 모든 메서드 참조입니다.

매우 유사한 방식으로 모든 메서드, 속성, 확장 및 클래스의 다른 멤버에 액세스 할 수 있습니다.

val bigDecimalClass = BigDecimal::class println(bigDecimalClass.constructors) println(bigDecimalClass.functions) println(bigDecimalClass.memberProperties) println(bigDecimalClass.memberExtensionFunctions)

3.2. Kotlin 메서드 참조

클래스와 상호 작용할 수있을뿐만 아니라 메서드 및 속성과 상호 작용할 수도 있습니다 .

여기에는 val 또는 var , 표준 클래스 메서드 및 최상위 함수로 정의 된 클래스 속성이 포함됩니다 . 이전과 마찬가지로 표준 Java로 작성된 코드에서도 Kotlin으로 작성된 코드에서와 마찬가지로 잘 작동합니다.

클래스와 똑같은 방식으로 :: 연산자를 사용하여 메서드 또는 속성에 대한 참조를 얻을 수 있습니다 .

이것은 메소드 참조를 얻기 위해 Java 8에서와 똑같아 보이며 똑같은 방식으로 사용할 수 있습니다. 그러나 Kotlin에서는이 메서드 참조를 사용하여 대상에 대한 반사 정보를 가져올 수도 있습니다.

메서드 참조를 얻은 후에는 실제로 해당 메서드 인 것처럼 호출 할 수 있습니다 . 이를 호출 가능한 참조라고합니다.

val str = "Hello" val lengthMethod = str::length assertEquals(5, lengthMethod())

클래스에 대해 할 수있는 것과 같은 방식으로 메서드 자체에 대한 자세한 내용을 얻을 수도 있습니다. 여기에는 표준 자바 세부 정보와 메소드가 연산자 인지 또는 인라인 인지 와 같은 Kotlin 관련 세부 정보가 모두 포함됩니다 .

val byteInputStream = String::byteInputStream assertEquals("byteInputStream", byteInputStream.name) assertFalse(byteInputStream.isSuspend) assertFalse(byteInputStream.isExternal) assertTrue(byteInputStream.isInline) assertFalse(byteInputStream.isOperator)

이 외에도이 참조를 통해 메서드의 입력 및 출력에 대한 자세한 정보를 얻을 수 있습니다 .

여기에는 null 허용 여부 및 선택 성과 같은 Kotlin 관련 세부 정보를 포함하여 반환 유형 및 매개 변수에 대한 세부 정보가 포함됩니다.

val str = "Hello" val method = str::byteInputStream assertEquals( ByteArrayInputStream::class.starProjectedType, method.returnType) assertFalse(method.returnType.isMarkedNullable) assertEquals(1, method.parameters.size) assertTrue(method.parameters[0].isOptional) assertFalse(method.parameters[0].isVararg) assertEquals( Charset::class.starProjectedType, method.parameters[0].type)

3.3. Kotlin 속성 참조

이것은 확실히 얻을 수있는 세부 사항은 다르지만 속성에서도 똑같이 작동합니다 . 대신 속성이 상수인지, 늦게 초기화되었는지 또는 변경 가능한지 알려줄 수 있습니다.

lateinit var mutableProperty: String val mProperty = this::mutableProperty assertEquals("mutableProperty", mProperty.name) assertTrue(mProperty.isLateinit) assertFalse(mProperty.isConst) assertTrue(mProperty is KMutableProperty)

Note that the concept of Properties also works in any non-Kotlin code. These are identified by fields that follow the JavaBeans conventions regarding getter and setter methods.

This includes classes in the Java standard library. For example, the Throwable class has a Property Throwable.message by virtue of the fact that there is a method getMessage() defined in it.

We can access the actual Property through Method references that are exposed – the getter and setter methods. The setter is only available if we are working with a KMutableProperty – i.e. the property was declared as var, whereas the getter is always available.

These are exposed in an easier to use way via the get() and set() methods. The getter and setter values are actual method references, allowing us to work with them exactly the same as any other method reference:

val prop = this::mutableProperty assertEquals( String::class.starProjectedType, prop.getter.returnType) prop.set("Hello") assertEquals("Hello", prop.get()) prop.setter("World") assertEquals("World", prop.getter())

4. Summary

이 문서에서는 표준 Java 언어에 내장 된 리플렉션 기능과 상호 작용하고 다른 기능을 포함하여 Kotlin에서 리플렉션으로 달성 할 수있는 몇 가지 사항에 대한 개요를 제공합니다.

모든 예제는 GitHub에서 사용할 수 있습니다.