목록분류 전체보기 (99)
안드로이드 개발자 노트
많은 개발자는 같은 알고리즘을 여러 번 반복해서 구현합니다. 여기서 말하는 알고리즘은 비즈니스 로직을 포함하는 특정 프로젝트에 국한된 것이 아닌, 수학적인 연산, 수집 처리처럼 별도의 모듈 또는 라이브러리로 분리할 수 있는 부분을 의미합니다. 예를 들어 Iterable.sorted와 같이 이미 있는 것을 활용하면, 단순하게 코드가 짧아진다는 것 이외에도 다양한 장점이 있습니다. 코드 작성 속도가 빨라진다. 호출을 한 번 하는 것이 알고리즘을 만드는 것보다 빠르다. 구현을 따로 읽지 않아도, 함수의 이름 등만 보고도 무엇을 하는지 확실하게 알 수 있다. 직접 구현할 때 발생할 수 있는 실수를 줄일 수 있다. 제작자들이 한 번만 최적화하면, 이러한 함수를 활용하는 모든 곳에 최적화의 헤택을 받는다. 표준 라..
"프로젝트에서 이미 있던 코드를 복사해서 붙여넣고 있다면, 무언가가 잘못된 것이다" 필자가 생각하는 프로그래밍의 가장 큰 규칙이며, 이를 'knowledge를 반복하여 사용하지 말라'라는 규칙으로 표현하고 있습니다. knowledge는 '의도적인 정보'를 뜻하며, 코드 또는 데이터로 표현할 수 있습니다. 또한, knowledge는 알고리즘의 작동 방식, UI의 형태, 원하는 결과 등 모든 의도적인 정보를 말합니다. 프로그램에서 중요한 knowledge를 크게 두 가지 뽑는다면, 다음과 같습니다. 로직(logic): 프로그램이 어떠한 식으로 동작하는지와 프로그램이 어떻게 보이는지 공통 알고리즘(common algorithm): 원하는 동작을 하기 위한 알고리즘 둘의 가장 큰 차이점은 시간에 따른 변화입니다..
코딩 컨벤션이란 읽고 관리하기 쉬운 코드를 작성하기 위한 일종의 코딩 스타일 규약입니다. 코틀린은 굉장히 잘 정리된 코딩 컨벤션을 갖고 있습니다. 코틀린뿐만 아니라, 어떤 개발 언어든 공식적인 컨벤션을 따르는게 좋습니다. 어떤 프로젝트를 접해도 쉽게 이해할 수 있다. 다른 외부 개발자도 프로젝트의 코드를 쉽게 이해할 수 있다. 다른 개발자도 코드의 작동 방식을 쉽게 추측할 수 있다. 코드를 병합하고, 한 프로젝트의 코드 일부를 다른 코드로 이동하는 것이 쉽다. IntelliJ 에서 설정이나 도구, 플러그인을 활용해 코틀린 컨벤션 검사를 할 수 있습니다. Setting ➡ Editor ➡ Code Style ➡ Kotlin 클릭 우측 상단 Set from... ➡ 원하는 스타일 가이드를 지정 Style i..
이름있는 아규먼트(네임드 파라미터)를 사용하면 코드가 길어지지만, 두 가지 장점이 생깁니다. 이름을 기반으로 값이 무엇을 나타내는지 알 수 있습니다. 파라미터 입력 순서와 상관 없으므로 안전합니다. 예제를 통해 살펴보겠습니다. val text = (1..10).joinToString("|") joinToString에 대해 이미 알고 있다면, "|"이 구분자(separator)를 의미한다는 것을 알 것입니다. 해당 함수에 대해서 잘 모른다면, 한눈에 파라미터가 명확하지 않게 보일 수도 있습니다. 파라미터가 명확하지 않은 경우에는 이를 직접 지정해서 명확하게 만들어 줄 수 있습니다. val text = (1..10).joinToString(separator = "|") 파라미터에 변수를 사용해서 더욱 의미를..
기본적으로 코틀린의 프로퍼티는 사용자 정의 게터와 세터를 가질 수 있습니다. var name: String? = null get() = field?.toUpperCase() set(value) { if (!value.isNullOrBlank()) { field = value } } 이 코드에서 field라는 식별자를 확인할 수 있습니다. 이는 프로퍼티의 데이터를 저장해두는 백킹 필드(backing field)에 대한 레퍼런스입니다. val을 사용해서 읽기 전용 프로퍼티를 만들 때는 field가 만들어지지 않습니다. val fullName: String get() = "$name $surname" var를 이용한 프로퍼티는 게터와 세터를 정의할 수 있으며, 이를 파생 프로퍼티(derived property..
코틀린에서는 특정 객체(타입)을 리시버(수신 객체)로 사용할 수 있습니다. // block: (T) -> R 일반적인 함수 정의 val block: (Int) -> Int = ... block(3) // block: T.() -> R 객체 T를 receiver 로 활용한다. val block: Int.() -> Int = ... 100.block(3) 이 경우에는 this를 사용해서 리시버를 참조할 수 있습니다. val sum = fun Int.(other: Int): Int { return this + other // 키워드 this 를 이용해 리시버 객체에 접근한다. } val result = 3.sum(5) print(result) // 8 this를 명시적으로 사용하지 않고 생략할 수도 있으며, ..
코틀린은 타입 추론 시스템을 갖추고 있습니다. 이는 개발 시간을 줄여줄 뿐만 아니라 유형이 명활할 때 코드가 짧아지므로 코드의 가독성이 크게 향상됩니다. 하지만 유형이 명확하지 않을 때는 남용하면 좋지 않습니다. val data = getSomeData() 가독성을 위해 코드를 설계할 때는 읽는 사람에게 중요한 정보를 숨겨서는 안 됩니다. 또한 가독성 향상 이외에 안전을 위해서도 타입을 지정하는 것이 좋습니다. 관련된 내용으로, 'item3: 최대한 플랫폼 타입을 사용하지 말라' 와 'item4: inferred 타입으로 리턴하지 말라'가 있겠습니다. 따라서 유형이 명확하지 않다면, 아래 처럼 타입을 명시해줍니다. val data: UserData = getSomeData()
코틀린에서는 void 대신, Unit 과 Nothing 이라는 타입을 제공해줍니다. Unit 은 함수가 끝났으나, 의미있는 반환값이 없는 경우 사용합니다. fun report() { // 아무것도 반환하지 않으면 return Unit 이 반환된다. } fun main(){ val result = report() println(result) // Kotlin.Unit } Nothing 은 함수가 끝이 나지 않는 경우 사용합니다. 예를 들면 throw Exception, while (true) { yield ... } 등이 있습니다. Nothing?은 모든 Nullable 타입을 상속받아서 사용합니다. 타입추론 과정에서 생긴 컴파일 오류를 명시적으로 만들어 줄 수 있습니다. var user = if ( is..
연산자 오버로딩은 굉장히 강력한 기능이지만, 위험할 수 있습니다. 예를 들어 팩토리얼을 구하는 함수를 생각해 봅시다. fun Int.factorial(): Int = (1..this).product() fun Iterable.product(): Int = fold(1) { acc, i -> acc * i } 이 함수는 Int로 정의되어 있으므로, 편리하게 사용할 수 있습니다. print(10 * 6.factorial()) // 7200 팩토리얼은 ! 기호를 사용해 표기합니다. 다음과 같이 연산자 오버로딩을 활용하면, 만들어낼 수 있습니다. operator fun Int.not() = factorial() print(10 * !6) // 7200 하지만 함수의 이름이 not이므로 논리연산에 맞게 사용해야지..
"개발자가 코드를 작성하는 데는 1분 걸리지만, 이를 읽는 데는 10분이 걸린다" 프로그래밍은 쓰기보다 읽기가 중요하다는 의미입니다. 항상 가독성을 생각하면서 코드를 작성해야 합니다. 인식 부하 감소 // 구현 A if (person != null && person.isAdult) { view.showPerson(person) } else { view.showError() } // 구현 B person?.takeIf { it.isAdult } ?.let(view::showPerson) ?: view.showError() 구현 A와 구현 B는 비교조차 할 수 없을 정도로 A가 훨씬 가독성이 좋은 코드입니다. 가독성이란 코드를 읽고 얼마나 빠르게 이해할 수 있는지를 의미하며, 이는 얼마나 많은 관용구(구조,..