목록Kotlin (69)
Hanbit the Developer
배경 개인적으로 Retrofit2의 구현을 분석한 적이 있는데 이때 suspend fun이 디컴파일될 때 마지막 인자로 Continuation이 붙으면서 처리된다는 것을 알게 되었고, 원리를 직접 알아보고 싶었습니다. 예시 1: 간단한 코루틴 다음과 같은 간단한 예시를 작성하였습니다. 기존 코드 class SampleViewModel : ViewModel() { fun logout() { viewModelScope.launch { val userId = getUserId() logoutUser(userId) } } private suspend fun getUserId(): Int = 0 private suspend fun logoutUser(userId: Int) { Log.d("SampleViewMod..
![](http://i1.daumcdn.net/thumb/C150x150/?fname=https://blog.kakaocdn.net/dn/cxwTKZ/btsEwGBHXL1/fyaxrkN1wdKONYMJ7ddCo0/img.png)
배경 구현을 하다보면 Room이나 DataStore에서의 Flow를 stateIn 함수를 통해 StateFlow로 변환하여 사용하는 경우가 잦다. public fun Flow.stateIn( scope: CoroutineScope, started: SharingStarted, initialValue: T ) scope, initialValue는 어떤 것인지 명확하게 알 수 있으나 started는 아니다. started 인자의 타입인 SharingStarted에 대해서 알아보고자 한다. Eagerly SharingStarted에는 3가지 companion object가 있다.(Eagerly, Lazily, WhileSubscribed) 첫번째는 Eagerly이다. Sharing is started imme..
![](http://i1.daumcdn.net/thumb/C150x150/?fname=https://blog.kakaocdn.net/dn/ewHHui/btsEqvnx4Pz/zYUPh6mWRnWrvi51lnOFqK/img.png)
배경 Flow는 구독(collect 등) 시작 시점에 동작을 시작하는 cold stream이고, StateFlow는 구독자의 구독 시점과 상관없이 동작을 시작할 수 있는 hot stream이다. 이 주제에 대해 말로만 인지하고 있었어서 코드로 확인해보고 싶었다. 동작 확인 Flow class FlowViewModel : ViewModel() { val tmp: Flow = flow { val nums = listOf(1,2,3,4,5,6,7,8,9,10) nums.forEach { delay(1000) emit(it) } } fun onClick() { viewModelScope.launch { tmp.collect { Log.e("tmp", it.toString()) } } } } 위 코드를 실행하고 ..
아이템 49: 하나 이상의 처리 단계를 가진 경우에는 시퀸스를 사용하라 *Sequences | Kotlin Documentation를 읽어보시는 것을 적극 권장합니다. Sequences는 Iterable과 매우 흡사하지만, 연산을 lazy하게 해서 더 효율적으로 처리하도록 설계되어 있으며, 다음과 같은 장점이 있다: 자연스러운 순서를 유지한다. 최소한만 연산한다. 무한 시퀸스 형태로 사용할 수 있다. 각각의 단계에서 컬렉션을 만들어 내지 않는다. 자연스러운 순서를 유지한다. .filter { print("F$it, "); it % 2 == 1 } .map { print("M$it, "); it * 2 } .forEach { print("E$it, ") } 위 코드를 listOf(1, 2, 3)와 sequ..
![](http://i1.daumcdn.net/thumb/C150x150/?fname=https://blog.kakaocdn.net/dn/c1zZwm/btstxmBOYQ9/AYd4omoogaei84zdfjwXck/img.png)
아이템 45: 불필요한 객체 생성을 피하라 객체 생성 비용 실제로 필요한 데이터보다 더 많은 용량을 차지한다. 추가적인 함수 호출이 필요하다. 생성되는 과정에 비용이 있다. 객체가 생성되고 메모리 영역에 할당되고 래퍼런스를 만드는 등의 작업이 필요하다. 객체 생성 줄이기 싱글톤 활용 예시: LinkedList 클래스에서 Empty가 자주 사용되기 때문에 object를 붙여 싱글톤으로 활용한다. 캐시 활용(Memoization): 메모리를 활용하여 시간을 줄인다. 메모리 관리를 위해 SoftReference 사용하면 좋다. *WeakReference vs SoftReference: WeakReference는 래퍼런스가 끊기고 GC 타이밍이 되면 GC를 곧바로 하지만, SoftReference는 다 같으나 ..
아이템 36: 상속보다는 컴포지션을 사용하라 상속의 장점 다형성을 활용할 수 있다. 상속의 단점 복잡한 계층구조가 만들어지기 쉽다. 인터페이스 분리 원칙 위배: 불필요한 것까지 가져온다. 코드를 읽기 어렵다. 컴포지션 장점 다른 클래스의 public한 것들에만 의존하므로 안전하다. 유연하다. 컴포지션 단점 상속 사용 시 보다 코드를 수정해야 하는 경우가 더 많다. → 일반적으로 컴포지션을 사용하는 것이 좋다. → 상속을 사용하는 경우: 명확한 ‘is-a’ 관계 아이템 37: 데이터 집합 표현에 data 한정자를 사용하라 data 한정자를 붙이면 다음과 같은 함수가 자동으로 생성된다: equals() hashCode() toString() copy() componentN() 장점: 가독성: Pair vs ..
아이템 33: 생성자 대신 팩토리 함수를 사용하라 장점은 다음과 같다: 함수에 네이밍 가능 → 가독성 확보(ex. ArrayList(3): 3이 무슨 의미일까? 의문 → ArrayList.withSize(3)) 다른 타입 리턴 가능(ex. listOf(): 각자의 플랫폼에서 다른 구현체를 반환할 수 있다.) 싱글톤 사용 가능 아직 존재하지 않는 객체 리턴 가능 → 빌드 이전에 코드에서 해당 타입을 사용할 수 있다는 뜻 가시성 제어 inline, reified 사용 가능 복잡한 객체 생성 생성자와 달리 슈퍼클래스나 기본생성자를 호출하지 않아도 됨 팩토리 함수 유형 companion 팩토리 함수 확장 팩토리 함수(fun Tool.Companion.createBigTool(): BigTool): compani..
아이템 26: 함수 내부의 추상화 레벨을 통일하라 올바른 예시: fun makeCoffee() { boilWater() brewCoffee() pourCoffee() } 잘못된 예시: fun makeCoffee() { // boil water val water = findWater() val kettle = findKettle() kettle.boil(water) brewCoffee() pourCoffee() } 아이템 27: 변화로부터 코드를 보호하려면 추상화를 사용하라 추상화 할 수 있는 방법은 다음과 같다: 상수로 추출한다. 동작을 함수로 래핑한다. 함수를 클래스로 래핑한다. 인터페이스 뒤에 클래스를 숨긴다. 보편적인 객체를 특수한 객체로 래핑한다. 다만 추상화가 과도하면 코드가 복잡해지기 때문에 ..
아이템 19: knowledge를 반복하여 사용하지 말라 프로그래밍의 가장 큰 규칙은 다음과 같다: 프로젝트에서 이미 있던 코드를 복사해서 붙여넣고 있다면, 무언가가 잘못된 것이다. knowledge 의도적인 정보를 뜻하며 코드나 데이터로 표현할 수 있다. 상속 시 오버라이드가 안 되게 강제한다는 것은 ‘해당 메서드가 슈퍼클래스와 동일하게 동작하기 원한다’는 knowledge를 함유한다. 알고리즘 작동 방식, UI 형태, 원하는 결과 등이 모두 의도적인 정보이다. 가장 중요한 knowledge 두 가지는 다음과 같다: 로직: 프로그램이 어떤 식으로 동작하는지, 프로그램이 어떻게 보이는지 공통 알고리즘: 원하는 동작을 하기 위한 알고리즘 둘의 차이점은, 전자의 경우 계속해서 변하지만 후자는 그렇지 않다는 ..
💡 컴퓨터가 인식할 수 있는 코드는 바보라도 작성할 수 있지만, 인간이 이해할 수 있는 코드는 실력 있는 프로그래머만 작성할 수 있다. - Martin Flowler, 아이템 11: 가독성을 목표로 설계하라 인식 부하 감소 일반적으로 자주 사용하는 패턴을 활용할 것 극단적이게 되지 않기 경험이 많은 코틀린 개발자만 쉽게 이해할 수 있는 코드가 있으면, 제너럴하게는 이해하기 어려운 코드가 된다. 그럼에도 불구하고 이러한 ‘비용’을 지불할 만한 이유가 있다면 트레이드 오프를 진행하는 옵션을 고려할 수 있다. 컨벤션 아이템 12: 연산자 오버로드를 할 때는 의미에 맞게 사용하라 factorial() 함수를 쉽게 표현하기 위해 operator fun Int.not() = factorial()을 작성하여 사용하면..