코틀린은 자바와의 상호 운용을 중요하시하는 언어이다.
따라서 JVM 상에서 두 언어의 유연함을 가지기 위해 Jvm Prefix Annotation을 제공한다.
주로 사용하는 5가지 Annotation에 대해 알아보자.
- JvmField
- JvmStatic
- JvmOverloads
- JvmName
- JvmSynthetic
1. JvmField
코틀린에는 프로퍼티라는 개념이 존재하지만, 자바에서는 필드라는 개념이 존재한다.
즉, 자동으로 getter/setter를 만들어주지 않는다.
따라서 코틀린의 프로퍼티를 자바에서 필드처럼 사용하고 싶다면 @JvmField 어노테이션을 사용한다.
class Person {
@JvmField
var name: String = "John Doe"
}
// 자바 코드에서 호출
Person person = new Person();
person.name = "Alice";
String name = person.name;
@JvmField 어노테이션을 붙여주지 않으면, 자바에서는 getName()과 setName() 메서드를 통해 name에 접근할 수 있다.
2. JvmStatic
@JvmStatic 어노테이션은 마치 자바의 static 메서드처럼 사용할 수 있게 만들어준다.
class Calculator {
companion object {
@JvmStatic
fun add(a: Int, b: Int): Int {
return a + b
}
}
}
// 자바 코드에서 호출
int result = Calculator.add(1, 2);
@JvmStatic 어노테이션을 붙여주지 않으면, Calculator.Companion.add() 형식으로 호출해야 한다.
디컴파일 결과, @JvmStatic 어노테이션이 붙은 메서드를 정적으로 만들어 Companion의 add() 메서드를 참조하도록 한다.
그렇기 때문에 외부에서는 굳이 Companion에 접근할 필요가 없다.
3. JvmOverloads
Java에는 불행하게도 파라미터에 default값을 설정할 수가 없다.
반면, Kotlin은 개발자의 편리함과 유연성을 위해 파라미터의 default 값 설정을 지원한다.
자바와의 상호 운용을 위해 @JvmOverloads 어노테이션을 사용하면, 자바에서도 마치 default값이 있는 메서드처럼 사용할 수 있다.
이는 컴파일러가 자동으로 오버로딩 해주기 때문이다.
class Person {
@JvmOverloads
fun greet(name: String = "World") {
println("Hello, $name!")
}
}
// Kotlin에서 호출
val person = Person()
person.greet() // "Hello, World!" 출력
person.greet("Alice") // "Hello, Alice!" 출력
// 자바 코드에서 호출
Person person = new Person();
person.greet(); // "Hello, World!" 출력
person.greet("Alice"); // "Hello, Alice!" 출력
Java에서도 Kotlin처럼 greet() 메서드를 동일한 조건으로 호출할 수 있다.
디컴파일 결과 name 파라미터를 입력받는 greet(String name) 메서드가 정의되어 있다.
그리고 맨 아래 아무 인자도 없는 greet() 메서드가 정의되어 있다.
자세히 살펴보면, 중간에 synthetic method가 존재한다.
default값으로 설정한 "World"를 내부에서 따로 초기화하고, Person의 greet(String name) 메서드에 해당 인자를 넘겨 호출한다.
내부적으로는 이러한 과정을 거치기에, Java에서 마치 default 값이 설정되어 있는 메서드처럼 호출할 수 있는 것이다.
@JvmName
@JvmName은 Kotlin의 getter/setter를 포함한 함수, .kt 파일명을 자바에서 어떻게 사용할 것인지 정의할 수 있다.
class Person {
@JvmName("greetWithName")
fun greet(name: String) {
println("Hello, $name!")
}
}
// 자바 코드에서 호출
Person person = new Person();
person.greetWithName("Alice"); // "Hello, Alice!" 출력
Person 클래스의 greet() 메서드에 @JvmName 어노테이션을 설정해 주었다.
Java에서는 greet()이 아닌, greetWithName() 이라는 이름으로 메서드를 호출할 수 있게 변경되었다.
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER, AnnotationTarget.FILE)
@Retention(AnnotationRetention.BINARY)
@MustBeDocumented
public actual annotation class JvmName(actual val name: String)
이 외에도 JvmName의 Target을 살펴보면 함수, 프로퍼티의 getter/setter, File에 대해서 적용할 수 있다.
예를 들어, 아래와 같은 코드도 가능하다는 것이다.
class Person {
@get:JvmName("hisName")
val name: String = "BuNa"
}
// 자바 코드에서 호출
Person person = new Person();
person.hisName();
사용 지점 대상(use-site target)으로 file과 getter의 이름을 변경해 주었다.
그 결과 자바에서는 hisName이라는 이름으로 name 프로퍼티에 접근할 수 있게 되었다.
또한, @file:JvmName은 .kt 파일의 이름을 변경할 수 있다.
Kotlin의 최상위 함수(top-level function)를 Java에서 접근하는 경우에 대비해서, 컴파일러는 파일명을 클래스명으로 만든다.
하지만 이런 방식이 마음에 들지 않을 수 있다.
// Test.kt
@file:JvmName("Greet")
fun hello() {
println("hello")
}
// 자바 코드에서 호출
// @JvmName 적용 전
new TestKt().hello();
// @JvmName 적용 후
new Greet().hello();
따라서 위와 같은 방식으로 변경할 수 있다.
@file:JvmName("Greet") 어노테이션을 적용함으로써, Test.kt 파일명에 대한 클래스명을 Greet으로 변경하였다.
TestKt라는 클래스명보다 Greet이라는 클래스명이 더 낫다는 것은 누구나 동의할 것이라고 생각한다.
@JvmSynthetic
Kotlin에는 Java와 달리, internal 접근 제한자가 존재한다.
internal 접근 제한자는 다른 모듈에서 접근하지 못하도록 제한하는 기능을 제공한다.
하지만 Java에는 internal 접근 제한자가 없기 때문에 다른 모듈에서의 접근을 어떻게 제한해야 할지 고민할 것이다.
이때 @JvmSynthetic 어노테이션을 사용하면 Java에서는 접근하지 못하도록 설정할 수 있다.
internal class Secret {
@JvmSynthetic
fun print() {
println("This is a secret!")
}
}
// 코틀린에서 호출
val secret = Secret()
secret.print() // "This is a secret!" 출력
// 자바 코드에서 호출
Secret secret = new Secret();
secret.print(); // 컴파일 에러 발생
하나의 사용 사례를 보여주기 위함이지, 반드시 internal 접근제한자에 대해서만 적용할 수 있는 것이 아니다.
필요에 따라 Java에서는 접근하지 못하도록 설정할 수 있다.
해당 포스팅 내용에 오류가 있다면 댓글로 남겨주세요 :)
github : https://github.com/tmdgh1592
'Programming > Kotlin' 카테고리의 다른 글
[Kotlin] Value Class (inline class deprecated) (2) | 2023.02.21 |
---|---|
[Kotlin] val a: Int = 1000과 val b: Int = 1000은 다르다 (2) | 2023.02.20 |
[Kotlin] const val vs val - 둘의 차이점은 무엇일까? (0) | 2023.02.11 |