Kotlin에서 구체화된 키워드 사용

David Mbochi Njonge 2023년10월12일
  1. Java 5 이전의 제네릭
  2. Java 5의 제네릭
  3. 유형 삭제란?
  4. 제네릭 함수에 클래스 전달
  5. Kotlin에서 inline 기능과 함께 reified 키워드 사용
  6. Kotlin에서 reified를 사용하여 동일한 입력으로 함수 오버로딩
  7. 결론
Kotlin에서 구체화된 키워드 사용

reified 키워드는 Kotlin에서 제네릭으로 작업하는 동안 주로 사용되는 프로그래밍 개념입니다.

애플리케이션에 유형 안전성을 추가하고 명시적 유형 캐스트를 방지하기 위해 일반 유형이 Java 5에 도입되었다는 것을 알고 있습니다.

제네릭의 한 가지 제한 사항은 제네릭 함수로 작업할 때 형식 정보에 액세스할 수 없다는 것입니다. 컴파일러는 Treified 유형 매개변수로 사용할 수 없으며 대신 클래스를 사용해야 한다고 불평합니다.

이 문제는 컴파일 중 유형 삭제로 인해 발생합니다. 이 자습서에서는 유형의 클래스를 일반 함수의 인수로 전달하고 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 키워드 사용

이것은 우리가 활용할 수 있는 두 번째 접근 방식이며 제네릭을 사용하여 유형 정보에 액세스할 때 가장 선호되는 방법입니다. reifiedinline 함수와 함께만 사용할 수 있으며 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 Mbochi Njonge avatar David Mbochi Njonge avatar

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