코틀린에서 자주 사용하는 어노테이션에 대해 알아본다
어노테이션은 메타데이터 ( 부가기능 )을 코드에 비침투적으로 추가할 수 있는 수단이다
코틀린에서 어노테이션(Annotation)의 종류
- Kotlin에 내장되어 있는 built in annotation
- Anotation의 정보를 알려주기 위한 meta annotation
- custom annotation
크게 보면 이렇게 세 종류의 어노테이션이 존재합니다.
Kotlin에 내장되어있는 built in Annotationd
코틀린에는 이미 무수한 annotation들이 내장되어 있습니다.
@Deprecated
특정 클래스, 함수, 변수 등을 더 이상 사용하지 말아달라고 말하고 싶을 때 사용하는 어노테이션입니다.
@Deprecated("It is deprecated")
fun sum(a: Int, b: Int) = a + b
@Deprecated이 되어있는 함수를 사용하면 다음과 같이 표현됩니다.
@JvmOverloads
@JvmOverloads는 함수 또는 생성자 파라미터에 default value가 설정되어 있을 경우
Kotlin Compiler가 default value 만큼 자동으로 Overloading 함수를 만들어주는 기능을 갖고 있다
예를 들어 Java에 다음과 같이 하나의 클래스가 여러 생성자를 갖고 있다고 생각하자
public class CustomView extends View {
// View를 인스턴스화
public CustomView(Context context) {
super(context);
}
// xml을 사용하여 View를 inflating 할 때
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
}
// 두 번째에 + defStyleAttr로 View의 기본 속성을 지정
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
이를 Kotlin으로 표현하면 다음과 같이 쓸 수 있다
class CustomView : View {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
// ...
}
여기서 @JvmOverloads를 사용하면 더욱 간단히 표현할 수 있다.
class CustomView @JvmOverloads
constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0)
: View(context, attrs, defStyleAttr) {
// ...
}
이를 공식화하면 다음과 같이 된다.
class SubClassName @JvmOverloads constructor(매개변수 목록) : SuperClassName(인자 목록)
@Qualifier
@Qualifier는 주로 Hilt나 Dagger2에서 사용되는 어노테이션이다.
Hilt나 Dagger2를 사용하여 의존성을 부여할 때
주로 의존성을 제공하는(@Provides) 함수의 반환하는 값을 보고 서로 의존성이 어디로 가는지 인식합니다.
하지만, 하나의 클래스 내에 동일한 형태의 객체를 반환하는 함수가 둘 이상 있을 경우
컴파일러는 어떤 함수를 제공하면 되는지(@Provides) 알 수 없습니다.
이때, 사용하는 것이 @Qulifier입니다.
예를 들어 다음과 같은 코드가 있다고 생각하면
@Module
public class NameModule{
@Provides
// String을 반환하는 함수1
String provideName1(){
return "Charles";
}
@Provides
// String을 반환하는 함수2
String provideName2(){
return "Runa";
}
}
같은 클래스 내에 String을 반환하는 함수가 2개 있습니다.
이 때 의존성 부여를 위해 아래와 같이 호출하면
class Main{
@Inject
// 이름은 name1으로 되어있지만 컴파일러는 이게 provideName1()의 반환 값이라는 것을 모른다.
String name1;
@Inject
String name2;
...
}
에러가 발생하게 됩니다.
이때, @Qualifier와 동일한 역할을 하는
기본적으로 javax.inject 패키지에서 제공하는 @Named 어노테이션을 사용하면
다음과 같이 표현할 수 있습니다.
@Module
public class NameModule{
@Provides
@Named("me")
String provideName1(){
return "Charles";
}
@Provides
@Named("you")
String provideName2(){
return "Runa";
}
}
class Main{
@Inject
@Named("me")
String name1;
@Inject
@Named("you")
String name2;
...
}
@Qualifier 커스텀하기
다음과 같은 방법으로 @Qualifier를 커스텀할 수 있습니다.
public class WantQuailfierAutowired{
// myTarget이라는 이름으로 지정
@Qualifier("myTarget")
private final Target taget;
// myTarget이라는 이름으로 호출
public WantQuailfierAutowired(@Qualifier("myTarget") Target target){
this.target = target;
}
}
Annotation에 제약을 거는 Meta Annotation
일반적으로 어노테이션을 정의할 때는 다음과 같이 사용합니다.
// 단순히 클래스 이름 앞에 annotation을 붙여주면된다
annotation class MyAnnotation
하지만, 해당 annotation에 다음과 같이 제약을 추가할 수 있는데
이를 Meta Annotation이라고 한다.
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@Repeatable
@MustBeDocumented
annotation class MyAnnotation
위 코드에서 정의한 각각의 Meta Annotation의 의미는 다음과 같습니다.
@Target
Annotation 이 어디에 사용 가능한지를 지정하는 데 사용됩니다.
주로 사용되는 파라미터는 아래와 같습니다.
- CLASS: class, interface, object, annotation class에 사용 가능하도록 제한
- FUNCTION: 생성자를 제외한 함수에만 사용 가능하도록 제한
- FIELD: backing field를 포함한 field 에만 사용 가능하도록 제한
- PROPERTY: 어노테이션을 프로퍼티에서만 사용할 수 있도록 제한
- TYPE: 모든 곳에 사용 가능하도록 제한
또한 위 코드에서 보이는 것처럼
다수의 Target을 지정할 수 있습니다.
@Retention
Annotation의 Scope를 제한하는 데 사용되고 파라미터에는 3가지가 있습니다.
- SOURCE
compile time 에만 유용하며 빌드된 binary 에는 포함되지 않습니다.
개발 중에 warning 이 뜨는 걸 보이지 않도록 하는 @suppress 와 같이 개발 중에만 유용하고, binary에 포함될 필요는 없는 경우에 사용합니다. - BINARY
compile time과 binary 에도 포함되지만 reflection을 통해 접근할 수는 없습니다. - RUNTIME: compile time 과 binary 에도 포함되고, reflection 을 통해 접근 가능합니다.
Custom Annotation에 @Retention을 표시해주지 않을 경우, 디폴트로 RUNTIME 이 됩니다.
@Repeatable
한 요소에 어노테이션이 중복으로 사용될 수 있는지을 나타낸다.
@MustBeDocumented
Generated Documentation에 해당 Annotation 도 포함될 수 있는지를 나타냅니다.
주로 Library를 만들 때 사용합니다.
다음과 같이 사용할 수 있습니다.
@MustBeDocumented
annotation class A_Annotation()
annotation class B_Annotation()
@A_Annotation
@B_Annotation
class TestClass()
A_Annotation() 클래스에는 @MustBeDocumented이 있지만 B_Annotation()에는 해당 어노테이션이 없습니다.
이를 Kotlin 공식 문서에 있는 dokka를 사용하면 공식 문서를 자동으로 만들 수 있습니다.
Build.gradle에 다음과 같이 설정한다.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath "org.jetbrains.dokka:dokka-gradle-plugin:0.9.14"
}
}
apply plugin: 'org.jetbrains.dokka'
dokka {
outputFormat = 'html'
outputDirectory = "$buildDir/html"
}
그리고 Terminal에 다음과 같이 입력하면
> ./gradlew dokka
다음과 같은 문서가 자동으로 생성됩니다.
자세히 보면 @MustBeDocumented를 붙인 @A_Annotation class만 문서화된 것을 확인할 수 있습니다.
코틀린 어노테이션 기본 사용 방법
코틀린에서 어노테이션은 [ ]을 사용해서 다수의 어노테이션을 사용할 수 있다
@을 사용해서 하나씩 적용할 수 도 있지만, [ ]을 사용해서 한 번에 다수의 어노테이션을 사용할 수 있다
// @Synchronized, @Strictfp와 같음
@[Synchronized Strictfp]
fun test() {
}
코틀린에서 어노테이션 정의하기
코틀린에 annotation의 정의는 클래스, 인터페이스 등의 앞에 annotation을 붙이면 된다
// annotation을 붙여서 정의
// annotation 파라미터는 항상 val을 붙여서 정의해야한다
annotation class MyAnnotation(val text: String) {
// 코틀린 1.3부터는 어노테이션에 동반객체를 본문 안에 넣을 수 있다
companion object {
}
}
// @을 사용하여 annotation 호출
@MyAnnotation("testText")
fun myAnnotation() {
}
2편에서는 어노테이션을 커스텀하는 방법과
커스텀한 어노테이션을 사용하는 방법에 대해 적어보겠습니다.
코틀린 어노테이션 정리
- 어노테이션은 컴파일러에게 코드 문법 에러를 체크하기 위한 정보 제공
- 개발 툴이나 빌더에게 코드 자동 추가를 위한 정보 제공
- 실행 시 특정 기능을 실행하기 위한 정보 제공
'코틀린' 카테고리의 다른 글
코틀린에서 자주 사용하는 어노테이션(Annotation)@ 정리-2 (0) | 2023.03.11 |
---|---|
안드로이트 코틀린 Reflection(리플렉션) 기초 정의 (0) | 2023.03.09 |
코틀린에서 변성(variance)이란 무엇인가 - 상세 설명 (0) | 2023.02.18 |
코틀린 확장함수 Scope함수 apply, with, let, also, run 이란? (0) | 2023.01.16 |
안드로이드 코틀린은 같은 변수를 계속 만들면 재활용할까? (0) | 2023.01.08 |
"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."
댓글