목록분류 전체보기 (140)
시배's Android
아이템45. 불필요한 객체 생성을 피하라 Int 와 같은 기본형을 사용하면, 일반적으로 기본 자료형 int 로 컴파일 된다. 하지만 nullable 로 만들거나, 타입 아규먼트로 사용할 경우에는 Integer 로 컴파일 된다. 기본 자료형은 null 일 수 없고, 타입 아규먼트로도 사용할 수 없기 때문이다. 객체 생성 비용은 항상 클까? 현대 64비트 JDK 에서 객체는 8바이트의 배수만큼 공간을 차지한다. 앞부분 12바이트는 헤더로서 반드시 있어야 하므로, 최소 크기는 16바이트이다. 추가로 객체에 대한 레퍼런스도 공간을 차지한다. 일반적으로 레퍼런스는 -Xmx32G 까지는 32/64비트 플랫폼 모두 4바이트다. 64비트 플랫폼에서는 32G(-Xmx32G)부터는 8바이트이다. 캐시를 활용하는 팩토리 함..
Timer TiTi 프로젝트에서는 Card를 이미지로 변환하여 갤러리에 저장하거나 인스타그램에 공유하는 기능을 추가하려고 합니다. 이를 위해서는 Composable 함수로 구현된 View를 Bitmap으로 변환하는 로직이 필요합니다. fun Modifier.createCaptureImageModifier(picture: Picture): Modifier = this.drawWithCache { val width = this.size.width.toInt() val height = this.size.height.toInt() onDrawWithContent { val pictureCanvas = Canvas( picture.beginRecording( width, height, ), ) draw(this,..
36. 상속보다는 컴포지션을 사용하라 상속은 관계가 명확하지 않을 때 사용하면, 여러 가지 문제가 발생할 수 있습니다. (‘is-a’ 관계에서 사용) class ProfilerLoader { fun load() { // 프로그래스 바를 보여 줌 // 프로파일을 읽어 들임 // 프로그레스 바를 숨김 } } class ImageLoader { fun load() { // 프로그래스 바를 보여 줌 // 이미지를 읽어 들임 // 프로그레스 바를 숨김 } } 상속을 이용하면 위의 코드를 아래와 같이 시용할 수 있습니다. abstract class LoaderWithProgress { fun load() { // 프로그래스 바를 보여 줌 innerLoad() // 프로그래스 바를 숨김 } abstract fun i..
아이템 33. 생성자 대신 팩토리 함수를 사용하라 클라이언트가 클래스의 인스턴스를 만들게 하는 가장 일반적인 방법은 기본 생성자(Primary constructor)를 사용하는 방법이다. class MyLinkedList{ val head : T val tail : MyLinkedList? } val list = MyLinkedList(1, MyLinkedList(2, null)) 하지만 생성자가 객체를 만들수 있는 유일한 방법은 아니다. 헬퍼 클래스를 생각해보자. fun myLinkedListOf( ): MyLinkedList? { if(elements.isEmpty()) return null val head = elements.first() val elementsTail = elements. copy..
아이템26. 함수 내부의 추상화 레벨을 통일하라 계층이 잘 분리되면 무엇이 좋을까요? 어떤 계층에서 작업할 때 그 아래의 계층은 이미 완성되어 있으므로, 해당 계층만 생각하면 된다는 것입니다. 즉, 전체를 이해할 필요가 없어지는 것입니다. 추상화 레벨 높은 레벨로 갈수록 물리 장치로부터 점점 멀어집니다. 프로그래밍에서는 일반적으로 높은 레벨일수록 프로세서로부터 멀어진다고 표현합니다. 높은 레벨일수록 걱정해야 하는 세부적인 내용들이 적습니다. 높은 레벨일수록 단순함을 얻지만, 제어력을 잃습니다. 예를 들어 C언어는 메모리 관리를 직접 할 수 있습니다. 반면, 자바는 가비지 컬렉터가 자동으로 메모리를 관리해 줍니다. 추상화 레벨 통일 함수도 높은 레벨과 낮은 레벨을 구분해서 사용해야 한다는 원칙이 있습니다...
import kotlin.reflect.KProperty fun main(){ val test : Int by LoggingProperty(100) println(test) } class LoggingProperty (var value : T) { operator fun getValue( thisRef : Any?, prop : KProperty ) : T { println("${prop.name} returned value $value") return value } operator fun setValue( thisRef : Any?, prop : KProperty, newValue : T ) { val name = prop.name println("$name changed from $value to $..
플로우는 비동기적으로 계산해야 할 값의 스트림을 나타냅니다. Flow 인터페이스 자체는 떠다니는 원소들을 모으는 역할을 하며, 플로우의 끝에 도달할 때까지 각 값을 처리하는 걸 의미합니다. 플로우를 사용하면 코루틴이 연산을 수행하는 데 필요한 기능을 전부 사용할 수 있습니다. 플로우의 빌더와 연산은 중단 함수이며 구조화된 동시성과 적절한 예외 처리를 지원합니다. 플로우는 어딘가에서 시작되어야 합니다. 플로우 빌더, 다른 객체에서의 변환, 또는 헬퍼 함수로부터 시작됩니다. 플로우의 마지막 연산은 최종 연산이라 불리며, 중단 가능하거나 스코프를 필요로 하는 유일한 연산이라는 점에서 아주 중요합니다. 시작 연산과 최종 연산 사이에 플로우를 변경하는 중간 연산을 가질 수 있습니다. 웹소켓이나 RSocket 알림..
핫 데이터 스트림은 열정적이라 데이터를 소비하는 것과 무관하게 원소를 생성하지만, 콜드 데이터 스트림은 게을러서 요청이 있을때만 작업을 수행하며 아무것도 저장하지 않습니다. 핫인 리스트와 콜드인 시퀀스를 사용할 때 그 차이가 드러납니다. 핫 데이터 스트림의 빌더와 연산은 즉각 실행됩니다. 콜드 데이터 스트림에서는 원소가 필요할 때까지 실행되지 않습니다. 콜드 데이터 스트림 무한할 수 있습니다. 최소한의 연산만 수행합니다. 메모리를 적게 사용합니다. 핫 데이터 스트림 항상 사용 가능한 상태입니다. 여러 번 사용되었을 때 매번 결과를 다시 계산할 필요가 없습니다. 채널은 핫이라 값을 곧바로 계산합니다. 별도의 코루틴에서 계산을 수행합니다. 채널은 소비되는 것과 상관없이 값을 생성한 뒤에 가지게 됩니다. 채널..