일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 메모이제이션
- Logback
- 구간 트리
- 주울
- 게이트웨이
- Spring Cloud Config
- Java
- Gradle
- spring boot
- 비트마스킹
- spring cloud
- 도커
- 서비스 디스커버리
- docker-compose
- Zuul
- 백트래킹
- 스택
- ZuulFilter
- dp
- 이분 매칭
- 다익스트라
- BFS
- 플로이드 와샬
- 트리
- 구현
- 이분 탐색
- 스프링 시큐리티
- 완전 탐색
- 달팽이
- 유레카
- Today
- Total
Hello, Freakin world!
코틀린으로 ModelMapper 만들기 - 2 본문
우선 1번 글에서 지적한 문제를 해결한 코드부터 살펴보겠습니다.
import kotlin.reflect.KParameter
class ModelMapper {
/**
* 반환 클래스의 주 생성자를 통해 값을 바인딩하고 반환하는 mapper
*/
inline fun <reified T, reified R> mapper(source: T): R {
R::class.constructors
.last { constructor ->
val mutableMap: MutableMap<KParameter, Any?> = mutableMapOf<KParameter, Any?>()
constructor.parameters.forEach { parameter ->
mutableMap[parameter] = T::class.members.find { it.name == parameter.name }?.call(source)
}
return constructor.callBy(mutableMap)
}
throw RuntimeException("${R::class.simpleName} 클래스의 생성자가 존재하지 않습니다.")
}
}
inline, reified 같은 자바에서는 볼 수 었었던 키워드들이 보이네요.
일단 이것들은 제쳐두고 중괄호 블록 안의 코드에 대해서 잠깐 설명을 해볼게요.
코틀린에선 :: 연산자를 통해 리플렉션 메서드를 호출할 수 있습니다.
아래는 타입파라미터 R 클래스의 생성자 중 가장 마지막 생성자에 접근해 람다를 파라미터로 주는 코드입니다.
주생성자가 있는 경우 마지막 생성자는 주생성자가 있을 경우, 항상 주생성자가 됩니다.
(* 코틀린에서 주생성자는 class 이름 옆에 바로 선언되는 생성자를 주생성자라고 합니다.)
R은 리턴될 타입을 가리키는 파라미터입니다.
대게 주생성자만 있는 data class인 경우가 많기 때문에 일단은 이런 식으로 구현했습니다.
R::class.constructors.last { constructor => ... }
그런데 잠깐!
뭔가 이상합니다. 자바에선 이러한 접근이 불가능하지 않았었나요?
자바에선 타입 파라미터에 리플렉션 API 접근이 불가능합니다.
제너릭 타입 이레이즈(Gereric type erase) 라는 과정이 컴파일에 포함돼있기 때문입니다. 이 과정은 런타임에 타입 정보를 지워버립니다.
그래서 자바로 코드를 작성할 때는 R로 타입을 지정하기만 할 뿐, R 타입에 리플렉션 API를 호출할 수가 없습니다.
하지만 코틀린에서는 가능합니다!
뭣때문에 가능하냐면 앞서 언급했던 inline, refied 라는 키워드 때문입니다.
inline 키워드는 함수 앞에 사용할 수 있습니다.
이를 인라인 함수라고 하는데, 이는 함수를 호출하는 곳에 함수 본문을 컴파일 시점에 끼워넣어버립니다.
바로 이 점 때문에, 코틀린은 타입 파라미터에 어떤 타입이 할당될 것인지 문맥을 유추할 수 있게 됩니다.
마치 런타임에 타입이 할당되는 것처럼, 타입 파라미터에 리플렉션 API를 호출해서 위와 같은 작업을 할 수 있는것이지요.
그리고 인라인 함수의 제네릭 타입에는 reified라는 키워드를 붙여줘야 합니다.
위의 저 함수 하나로 간단한 모델 매퍼를 만들어 봤는데요.
아래는 모델 매퍼를 사용하는 간단한 예입니다.
data class UserModel(
val name: String,
val age: Int
)
fun findUser(id: Long) : UserModel {
return userRepository.findById(id)
.map { modelMapper.mapper<User, UserModel>(it) }
.orElseThrow { NotFoundDataException("해당 아이디의 사용자 정보를 찾을 수 없습니다. id : $id") }
}
data class는 더이상 리플렉션을 위한 의미없는 인자가 없는 생성자를 생성할 필요가 없습니다.
단지 객체를 초기화할 필드를 모두 주생성자에 포함시키면 됩니다.
자바에선 mapper 인자에 UserModel.class 처럼 타입에 대한 정보를 Class<T> 로 받았지만, inline, reified 키워드로 이 역시도 필요가 없어졌습니다.
단지 파라미터를 전달하는 것만으로 이를 대체할 수 있습니다!
'프로그래밍 언어 > 코틀린' 카테고리의 다른 글
코틀린으로 ModelMapper 만들기 - 1 (0) | 2022.03.17 |
---|