자바 Static, Kotlin Companion, 그리고 Annotation의 Function
DoDoBest
·2024. 3. 16. 00:00
아직 작성 중인 미완성 글입니다.
Annotation 파라미터 특징
애노테이션 인자는 컴파일 시점에 그 값이 무엇인지 알 수 있어야 합니다. 따라서 임의의 프로퍼티를 인자로 지정할 수는 없습니다. 프로퍼티를 애노테이션 인자로 사용하려면 그 앞에 const 변경자(컴파일러는 const가 붙은 프로퍼티를 컴파일 시점 상수로 취급합니다.)를 붙여야 합니다.
const val TEST_TIMEOUT = 100L
@Test(timeout = TEST_TIMEOUT) fun testMethod() { ... }
함수는 상수가 아닌 걸까요?
JUnit5 테스트 코드를 작성할 때, ParameterizedTest에서 MethodSource의 인자로 전달하는 함수에 JvmStatic 애노테이션을 붙이지 않으면 테스트 코드가 동작하지 않습니다.
함수 코드는 컴파일 시점에 분명하게 알 수 있는데, JvmStatic을 붙이지 않으면 에러가 발생하는 이유는 무엇일까요? 함수는 상수가 아닌걸까요?
@ParameterizedTest
@MethodSource("createInputAboutBracketsWithAnswer")
@DisplayName("정상적인 괄호 연산식을 입력하면 연산 결과를 변환한다")
fun `test for input with brackets`(data: Pair<String, Double>) {
// given
val (input, expected) = data
// when
val actual = calculator.calculate(input)
// then
assertThat(actual).isEqualTo(expected)
}
companion object {
@JvmStatic // 이 애노테이션을 붙이지 않으면 에러 발생
fun createInputAboutBracketsWithAnswer(): List<Pair<String, Double>> {
return listOf(
"(1.4)" to 1.4,
"(1)" to 1.0,
)
}
}
MethodSource 파라미터 조건
JUnit5 공식 문서에서 MethodSource 애노테이션은 test class 또는 external class의 factory method를 참조할 수 있게 해준다고 설명되어 있습니다.
@MethodSource allows you to refer to one or more factory methods of the test class or external classes.
또한 Test 클래스 앞에 @TestInstance(Lifecycle.PER_CLASS) 애노테이션을 붙이지 않는한, factory method는 static이어야 하며, external 클래스의 factory method는 반드시 항상 static 이어야 한다고 명시되어 있습니다.
상수의 정의
Constant 상수는 Stack, Heap 메모리 영역에 올린 후 실행해서 해석하는 과정 없이 기계어 자체만으로 의미를 알 수 있는 것을 말합니다.
예를 들어 아래와 같은 상수는 기계어로 보든, 데이터 영역에 로드하든 1이라는 것을 바로 알 수 있습니다.
private const val NUM = 1
반면 아래와 같은 함수는 기계어 글자 만으로는 어떤 의미인지 알 수 없습니다.
우리가 눈을 통해 코드 한 줄 한 줄 읽어가며 listOf("a", "bc")를 반환한다는 것을 해석하는 것처럼, 컴파일러도 기계어를 메모리에 올려 어떤 값을 반환하는지 해석하는 과정이 필요합니다.
fun createData(): List<String> {
val returnValue = mutableListOf<String>()
returnValue.add("a")
val sb= StringBuilder()
sb.append("b")
sb.append("c")
returnValue.add(sb.toString())
return returnValue
}
아래와 같이 외부 서버로부터 값을 받아오는 함수는 실행을 하고나서야 리턴 값을 알 수 있다는 사실이 더욱 분명합니다.
fun fetchUserInfo(): String {
val userInfo = userRepository.getUserInfo()
return userInfo.name
}
오해한 개념 : First class != 상수
Kotlin은 일급 함수(first class) 특징을 가집니다. 함수를 변수에 저장할 수 있고, 파라미터로 전달할 수 있으며, 함수의 리턴 값으로 함수를 반환할 수도 있습니다.
변수처럼 다룰 수 있다 + 상수처럼 개발자가 작성한 코드는 런타임에 바뀌지 않는다
두 특징을 종합하여 함수는 상수라고 생각했습니다.
이전 상수의 정의를 통해 함수는 상수가 아닌 이유를 알아봤고, 일급 함수는 단지 특징의 하나일 뿐 상수의 정의를 만족하지 않음을 알 수 있습니다.
애노테이션에 함수 직접 넣어보기
(애토네이션에 함수를 넣을 수 없는 이유 GPT 답변 참고해서 정리하기)
자바 애노테이션 인터페이스에서는 함수를 선언할 수 있는데, 안 되는 이유 찾아봐야함
method를 입력받는 애노테이션 직접 만들기
MethodSource의 파라미터는 왜 꼭 static 이어야 하는 걸까요? 직접 method를 입력받는 애노테이션을 만들어 보면서 확인해보겠습니다.
MethodSource는 java로 작성되어 있으며, 다음과 같이 구현되어 있습니다.
Kotlin에서는 public @interface가 아닌 annotation class로 선언합니다.
@Target(AnnotationTarget.FUNCTION)
annotation class CustomAnnotation
자바로 디컴파일된 코드를 확인해보면 다음과 같습니다.
Retention은 정의한 애노테이션 클래스를 소스 수준에서만 유지할지, .class 파일에 저장할지, 실행 시점에 리플렉션을 사용해 접근할 수 있게 할지를 지정하는 메타애노테이션입니다. 코틀린에서는 명시하지 않으면 기본적으로 RUNTIME으로 설정합니다.
'학습' 카테고리의 다른 글
Kotlin companion object vs Kotlin object in class vs Java static (0) | 2024.03.22 |
---|---|
EditText가 입력된 text를 복원하는 과정 (0) | 2024.03.20 |
for .. in 은 무엇일까 (0) | 2024.03.08 |
Android에서 ConstraintLayout은 왜 사용하는 걸까 (0) | 2024.03.03 |
Android에서 View는 어떻게 그려질까? - 1 (0) | 2024.02.28 |