수색…
소개
리플렉션은 컴파일 타임 대신 런타임에 코드를 검사 할 수있는 언어의 기능입니다.
비고
리플렉션은 런타임에 언어 구성 (클래스 및 함수)을 인트로 스 케 츠 (searrospect)하는 메커니즘입니다.
JVM 플랫폼을 대상으로 할 때 런타임 리플렉션 기능은 별도의 JAR ( kotlin-reflect.jar
됩니다. 이는 런타임 크기를 줄이고 사용되지 않는 기능을 제거하며 JS와 같은 다른 플랫폼을 대상으로 삼을 수 있도록하기 위해 수행됩니다.
클래스 참조하기
어떤 클래스를 나타내는 KClass
객체에 대한 참조를 얻으려면 이중 콜론을 사용하십시오.
val c1 = String::class
val c2 = MyClass::class
함수 참조하기
기능은 Kotlin의 일류 시민입니다. 이중 콜론을 사용하여 참조를 얻은 다음 다른 함수로 전달할 수 있습니다.
fun isPositive(x: Int) = x > 0
val numbers = listOf(-2, -1, 0, 1, 2)
println(numbers.filter(::isPositive)) // [1, 2]
자바 리플렉션과 상호 운용
Kotlin의 KClass
에서 Java의 Class
객체를 얻으려면 .java
확장 속성을 사용하십시오.
val stringKClass: KClass<String> = String::class
val c1: Class<String> = stringKClass.java
val c2: Class<MyClass> = MyClass::class.java
후자의 예제는 중간 KClass
인스턴스를 할당하지 않기 위해 컴파일러에 의해 최적화 될 것이다.
클래스의 모든 속성 값 가져 오기
주어진 Example
클래스는 몇 가지 속성을 가진 BaseExample
클래스를 확장합니다 :
open class BaseExample(val baseField: String)
class Example(val field1: String, val field2: Int, baseField: String):
BaseExample(baseField) {
val field3: String
get() = "Property without backing field"
val field4 by lazy { "Delegated value" }
private val privateField: String = "Private value"
}
클래스의 모든 속성을 유지할 수 있습니다.
val example = Example(field1 = "abc", field2 = 1, baseField = "someText")
example::class.memberProperties.forEach { member ->
println("${member.name} -> ${member.get(example)}")
}
이 코드를 실행하면 예외가 발생합니다. 속성 private val privateField
는 private로 선언되고 member.get(example)
을 호출하면 성공하지 못합니다. 이것을 처리하여 개인 속성을 필터링하는 한 가지 방법. 이를 위해 속성의 Java getter의 가시성 수정자를 확인해야합니다. private val
경우 getter가 존재하지 않으므로 개인 액세스를 취할 수 있습니다.
도우미 기능과 사용법은 다음과 같습니다.
fun isFieldAccessible(property: KProperty1<*, *>): Boolean {
return property.javaGetter?.modifiers?.let { !Modifier.isPrivate(it) } ?: false
}
val example = Example(field1 = "abc", field2 = 1, baseField = "someText")
example::class.memberProperties.filter { isFieldAccessible(it) }.forEach { member ->
println("${member.name} -> ${member.get(example)}")
}
또 다른 방법은 리플렉션을 사용하여 개인 속성에 액세스 할 수있게 만드는 것입니다.
example::class.memberProperties.forEach { member ->
member.isAccessible = true
println("${member.name} -> ${member.get(example)}")
}
클래스의 모든 속성 값 설정
예를 들어, 샘플 클래스의 모든 문자열 속성을 설정하려고합니다.
class TestClass {
val readOnlyProperty: String
get() = "Read only!"
var readWriteString = "asd"
var readWriteInt = 23
var readWriteBackedStringProperty: String = ""
get() = field + '5'
set(value) { field = value + '5' }
var readWriteBackedIntProperty: Int = 0
get() = field + 1
set(value) { field = value - 1 }
var delegatedProperty: Int by TestDelegate()
private var privateProperty = "This should be private"
private class TestDelegate {
private var backingField = 3
operator fun getValue(thisRef: Any?, prop: KProperty<*>): Int {
return backingField
}
operator fun setValue(thisRef: Any?, prop: KProperty<*>, value: Int) {
backingField += value
}
}
}
변경할 수있는 속성을 가져 오려면 모든 속성을 가져 와서 유형별로 변경할 수있는 속성을 필터링합니다. 비공개 속성을 읽으면 런타임 예외가 발생하므로 가시성도 확인해야합니다.
val instance = TestClass()
TestClass::class.memberProperties
.filter{ prop.visibility == KVisibility.PUBLIC }
.filterIsInstance<KMutableProperty<*>>()
.forEach { prop ->
System.out.println("${prop.name} -> ${prop.get(instance)")
}
모든 String
속성을 "Our Value"
로 설정하려면 반환 유형별로 추가 필터링을 할 수 있습니다. Kotlin은 Java VM에 근거하고 있으므로, Type Erasure 가 유효 해, List<String>
등의 범용 형을 돌려주는 Properties는 List<Any>
. 슬프게도 반사는 황금의 총알이 아니며이를 피할 수있는 합리적인 방법이 없으므로 사용 사례에서주의해야합니다.
val instance = TestClass()
TestClass::class.memberProperties
.filter{ prop.visibility == KVisibility.PUBLIC }
// We only want strings
.filter{ it.returnType.isSubtypeOf(String::class.starProjectedType) }
.filterIsInstance<KMutableProperty<*>>()
.forEach { prop ->
// Instead of printing the property we set it to some value
prop.setter.call(instance, "Our Value")
}