[Kotlin] Kotlin Annotations

케니스·2021년 9월 10일
0

안녕하세요 케니스입니다 👋

이번에는 kotlin.jvm 패키지에 있는 코틀린 코드를 자바에서 활용할 때 유용하고 자주 사용되는 어노테이션에 대해서 알아보겠습니다.

목록

  • @JvmName
  • @JvmField
  • @JvmStatic
  • @JvmOverloads
  • @Throws

@JvmName

@JvmName 은 Java의 클래스 또는 메소드의 이름을 지정합니다.

보통 @JvmName 은 Kotlin 메서드 또는 클래스를 JVM 바이트코드로 변환 하고 Java에서 호출되는 Kotlin 함수나 클래스의 이름이 동일한 시그니처일 때 이를 처리하기 위해서 사용합니다.

아래코드는 같은 이름을 가진 함수의 다형성 예시입니다.

fun foo(names: List<String>): List<String>
fun foo(numbers: List<Int): List<Int>

위 코드를 컴파일 하면 아래와 같은 에러를 발생하게 됩니다.

Error:(7, 1) Kotlin: Platform declaration clash: The following declarations have the same JVM signature (foo(Ljava/util/List;)V):
    fun foo(a: List<Int>): Unit defined in foo.main.kotlin in file kotlin.kt
    fun foo(a: List<String>): Unit defined in foo.main.kotlin in file kotlin.kt

이유는 바이트코드 생성시 List의 Generic 값은 구분되지 않기 때문에 두개가 동일한 메서드로 판단되기 때문이다. 이럴 때 @JvmName 어노테이션을 사용하여 이름을 지정해주면 해결된다.

@JvmName(name = "names")
fun foo(names: List<String>): List<String>
@JvmName(name = "numbers")
fun foo(numbers: List<Int): List<Int>

@JvmField

Kotlin 컴파일러에게 @JvmField 어노테이션을 선언한 프로퍼티에 대해 getters/setters를 생성하지 않게 요청합니다.

@JvmField 어노테이션 사용 제약은 다음과 같습니다.

  • backing field를 가지고 있어야함
  • private 는 불가
  • open override const 키워드가 아닌 경우
  • delegate 프로퍼티가 아닌 경우
	class Store(
    val name: String,
    @JvmField val latitude: Long
  )

Java에서 다음과 같이 사용할 수 있습니다.

public void main() {
		Store store = new Store("Cafe", 128.1234567)
    String name = store.getName();
  	long latitude = store.latitude;
}

@JvmStatic

함수 또는 프로퍼티에 static으로 접근 가능한 getter / setter 를 생성합니다.

	object Log {
			fun d(log: String) {
         println("d: $log")
      }
    
    	@JvmStatic
    	fun e(log: String) {
        println("e: $log")
      }
	}

위의 코드의 @JvmStatic 선언되어 있지 않은 d 함수를 호출할 때 kotlin, java에서 각 아래와 같이 호출됩니다.

// Java
public void main() {
  Log.INSTANCE.d("log")
}
// Kotlin
fun main() {
	Log.d("log")
}

Java 에서 Kotlin 함수 또느 프로퍼티를 static하게 사용하려면 @JvmStatic 어노테이션을 사용하면 됩니다.

@JvmStatic 선언된 Log object 클래스의 e 함수를 실행하면 다음과 같이 사용할 수 있습니다.

// Java
public void main() {
	Log.e("log")
}

@JvmOverloads

kotlin 클래스의 오버로딩 메소드들을 생성해주는 어노테이션입니다.

kotlin 클래스는 생성자에 기본인자를 정의하고 인스턴스를 생성할 때 다음과 같이 사용할 수 있습니다.

class User constructor(
    val id : Int = 0,
    val name: String = "Kenneth"
)

fun main() {
    User(id = 0)
    User(name = "Kenneth")
    User(id = 0, name = "Kenneth")
}

Java에서는 기본인자를 제공하지 않기 때문에 kotlin 으로 작성된 User 클래스의 인스턴스를 생성시 모든 파라미터를 작성해주어야 하는 불편함이 있습니다.

public void main() {
	  new User(0) // compile error
    new User(0, "Kenneth")
}

이 때 @JvmOverloads 어노테이션을 추가하면 kotlin compiler 가 자동으로 다양한 생성자 오버로드 함수들을 만들어 줍니다.

class User @JvmOverloads constructor(
    val id : Int = 0,
    val name: String = "Kenneth"
)

@JvmOverload 적용 후에는 Java에서 id의 값만 가지는 User 인스턴스도 생성이 가능해집니다.

public void main() {
	  new User(0)
    new User(0, "Kenneth")
}

@Throws

@Throws는 컴파일 타임에 코틀린 함수가 예외를 던질 수 있다는 것을 의미하는 어노테이션입니다.

Java 에서 throws와 같은 키워드는 Kotiln 에 없기 때문에 @Throws 를 사용합니다

@Throws(RuntimeException::class)
fun execute()

마무리

명확한 사용의도를 알지 못하고 사용했을 때 이해가 안가는 부분도 많았지만

이렇게 살펴보니 어떤 목적을 위해 바르게 써야하는지 익힐 수 있는 경험이 었습니다

다들 즐거운 코딩하세요 🤗

profile
노력하는 개발자입니다.

0개의 댓글