Kotlin에서 구체화된 키워드 사용
- Java 5 이전의 제네릭
- Java 5의 제네릭
- 유형 삭제란?
- 제네릭 함수에 클래스 전달
-
Kotlin에서
inline
기능과 함께reified
키워드 사용 -
Kotlin에서
reified
를 사용하여 동일한 입력으로 함수 오버로딩 - 결론
reified
키워드는 Kotlin에서 제네릭으로 작업하는 동안 주로 사용되는 프로그래밍 개념입니다.
애플리케이션에 유형 안전성을 추가하고 명시적 유형 캐스트를 방지하기 위해 일반 유형이 Java 5에 도입되었다는 것을 알고 있습니다.
제네릭의 한 가지 제한 사항은 제네릭 함수로 작업할 때 형식 정보에 액세스할 수 없다는 것입니다. 컴파일러는 T
를 reified
유형 매개변수로 사용할 수 없으며 대신 클래스를 사용해야 한다고 불평합니다.
이 문제는 컴파일 중 유형 삭제로 인해 발생합니다. 이 자습서에서는 유형의 클래스를 일반 함수의 인수로 전달하고 reified
키워드를 inline
함수와 함께 사용하는 것을 포함하여 두 가지 접근 방식을 사용하여 이 문제를 해결하는 방법을 배웁니다.
Java 5 이전의 제네릭
Java 5 이전에는 제네릭이 존재하지 않았기 때문에 목록 구현이 String
, Integer
, Objects
또는 기타 유형의 목록인지 알 수 없었습니다.
이러한 이유로 우리는 항상 명시적으로 원하는 유형으로 캐스팅해야 했습니다. 이러한 응용 프로그램은 잘못된 캐스트에 대한 유효성 검사가 없었기 때문에 RuntimeException
에 취약했습니다.
다음 예제는 Java 5 이전에 컬렉션이 구현된 방식을 보여줍니다.
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add("John");
list.add(3);
list.add(5);
String john = (String) list.get(2);
}
}
Java 5의 제네릭
제네릭은 이 문제를 해결하기 위해 Java 5에 도입되었습니다. 제네릭을 사용하면 특정 유형의 컬렉션을 정의할 수 있으며 유효하지 않은 유형을 입력하려고 하면 컴파일러에서 경고를 표시합니다.
제네릭은 또한 컬렉션에서 요소를 검색하는 데 명시적 유형 캐스트가 필요하지 않았기 때문에 RuntimeException
문제를 해결했습니다. 다음 예제는 컬렉션이 Java 5 및 이전 버전에서 구현된 방식을 보여줍니다.
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("John");
// list.add(3); // Required type String
// list.add(true); // Required type String
String john = list.get(0);
System.out.println(john);
}
}
유형 삭제란?
유형 삭제는 위에서 언급한 문제를 해결하기 위해 Java 5에 도입된 기능입니다.
IntelliJ로 이동하여 파일
> 새로 만들기
> 프로젝트
를 선택합니다. 프로젝트 이름
섹션에 프로젝트 이름을 reified-in-kotlin
으로 입력합니다.
Language
섹션에서 Kotlin
을 선택하고 Build system
섹션에서 Intellij
를 선택합니다. 마지막으로 만들기
버튼을 눌러 새 프로젝트를 생성합니다.
kotlin
폴더 아래에 Main.kt
파일을 만들고 다음 코드를 복사하여 파일에 붙여넣습니다.
fun <T> showType(t: T){
println(t);
}
fun main(){
showType("This is a generic function");
}
Java 5 이전에는 유형 정보가 없었으므로 Java 컴파일러는 유형 정보를 기본 개체 유형 및 필요한 유형 캐스트로 대체했습니다.
Java 내부에서 발생하는 상황을 보려면 코드를 실행하고 도구
> Kotlin
> Show Kotlin Bytecode
를 누르십시오. 열리는 창에서 디컴파일
을 눌러 아래와 같이 컴파일된 Java 코드를 봅니다.
public final class MainKt {
public static final void showType(Object t) {
System.out.println(t);
}
public static final void main() {
showType("This is a generic function");
}
// $FF: synthetic method
public static void main(String[] var0) {
main();
}
}
showType()
메서드에 전달한 유형 매개변수는 Object
로 변환되므로 유형 정보에 액세스할 수 없습니다. 이것이 제네릭으로 작업하는 동안 유형 삭제가 발생하는 방식입니다.
제네릭 함수에 클래스 전달
이것은 다음 섹션에서 다룰 reified
키워드를 사용하는 것만큼 효율적이지는 않지만 유형 삭제를 피하기 위해 사용할 수 있는 첫 번째 접근 방식입니다.
삭제된 제네릭 타입에 접근하기 위해 아래와 같이 제네릭 함수의 매개변수로 제네릭 타입 클래스를 전달할 수 있습니다.
이전 예제에 주석을 달고 Main.kt
파일에 다음 코드를 복사하여 붙여넣습니다.
fun <T> showType(clazz: Class<T>){
println(clazz);
}
fun main(){
showType(String::class.java)
}
이 접근 방식을 사용하면 제네릭 함수를 만들 때마다 제네릭 형식에 대한 클래스를 전달해야 하는데, 이는 우리가 원하는 것이 아닙니다.
이 코드를 실행하고 코드가 다음을 출력하는지 확인하십시오.
class java.lang.String
Kotlin에서 inline
기능과 함께 reified
키워드 사용
이것은 우리가 활용할 수 있는 두 번째 접근 방식이며 제네릭을 사용하여 유형 정보에 액세스할 때 가장 선호되는 방법입니다. reified
는 inline
함수와 함께만 사용할 수 있으며 Kotlin에서는 사용할 수 있지만 Java에서는 사용할 수 없습니다.
이전 예제에 주석을 달고 Main.kt
파일에 다음 코드를 복사하여 붙여넣습니다.
inline fun < reified T> showType(){
println(T::class.java);
}
fun main(){
showType<String>()
}
inline
기능은 reified
키워드가 유형 정보에 액세스하여 사용된 모든 위치에 inline
기능 본문을 복사하도록 도와줍니다.
위의 코드를 실행하고 위에서 다룬 것과 동일한 단계를 사용하여 디컴파일하십시오. 디컴파일된 코드는 아래와 같습니다.
public final class MainKt {
// $FF: synthetic method
public static final void showType() {
int $i$f$showType = 0;
Intrinsics.reifiedOperationMarker(4, "T");
Class var1 = Object.class;
System.out.println(var1);
}
public static final void main() {
int $i$f$showType = false;
Class var1 = String.class;
System.out.println(var1);
}
// $FF: synthetic method
public static void main(String[] var0) {
main();
}
}
우리가 제네릭 매개변수에 reified
키워드를 사용하여 inline
함수 showType()
를 정의했기 때문에 컴파일러는 함수의 본문을 복사하고 실제 선언된 유형으로 대체하였습니다. 이것은 final
메소드인 main()
에서 보여집니다.
선언된 유형에 액세스하면 클래스를 매개변수로 전달하지 않고도 일반 함수에서 유형 정보를 검색할 수 있습니다.
위의 코드를 실행하여 인자로 전달한 타입 파라미터인 String
의 타입 정보에 접근할 수 있는지 확인합니다.
class java.lang.String
Kotlin에서 reified
를 사용하여 동일한 입력으로 함수 오버로딩
일반 일반 함수로 작업하는 동안 동일한 입력으로 함수를 오버로드하고 다른 유형을 반환할 수 없지만 reified
키워드를 사용하여 이를 달성할 수 있습니다.
이전 예제에 주석을 달고 Main.kt
파일 뒤의 파일에 다음 코드를 복사하여 붙여넣습니다.
inline fun <reified T>computeResult(theNumber: Float): T{
return when(T::class){
Float::class -> theNumber as T
Int::class -> theNumber.toInt() as T
else -> throw IllegalStateException("")
}
}
fun main(){
val intResult: Int = computeResult(123643F);
println(intResult);
val floatResult: Float = computeResult(123643F);
println(floatResult);
}
보이지 않는 곳에서 computeResult()
메서드를 호출하면 컴파일러는 첫 번째 호출에서 Int
반환 유형을, 두 번째 호출에서 Float
반환 유형을 기대합니다.
컴파일러는 이 정보를 사용하여 함수 본문을 복사하는 동안 제네릭 형식을 예상 파일로 바꿉니다.
reified
를 사용하는 동안 큰 기능을 사용하는 것은 애플리케이션에 성능 문제를 야기하므로 좋은 방법이 아닙니다. 인라인
기능이 작은지 확인하십시오.
결론
그래서 우리는 Kotlin에서 유형 정보에 액세스하기 위해 inline
함수와 함께 reified
키워드를 사용하는 방법을 배웠습니다.
런타임 시 타입 정보 삭제의 원인이 되는 타입 삭제에 대해서도 알아보았습니다. 마지막으로 다른 유형을 반환하기 위해 동일한 입력으로 reified
를 사용하여 inline
함수를 오버로드하는 방법을 다루었습니다.
David is a back end developer with a major in computer science. He loves to solve problems using technology, learning new things, and making new friends. David is currently a technical writer who enjoys making hard concepts easier for other developers to understand and his work has been published on multiple sites.
LinkedIn GitHub