목록Kotlin (81)
안드로이드 개발자 노트
이름있는 아규먼트(네임드 파라미터)를 사용하면 코드가 길어지지만, 두 가지 장점이 생깁니다. 이름을 기반으로 값이 무엇을 나타내는지 알 수 있습니다. 파라미터 입력 순서와 상관 없으므로 안전합니다. 예제를 통해 살펴보겠습니다. 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가 훨씬 가독성이 좋은 코드입니다. 가독성이란 코드를 읽고 얼마나 빠르게 이해할 수 있는지를 의미하며, 이는 얼마나 많은 관용구(구조,..
코드를 안전하게 만드는 가장 궁극적인 방법은 다양한 종류의 테스트를 하는 것입니다. 이러한 종류의 테스트는 사용자의 관점에서 애플리케이션 외부적으로 제대로 작동하는지 확인하는 것이 목표입니다. 단위 테스트는 개발자가 만들고 있는 요소가 제대로 동작하는지를 빠르게 피드백해주므로 개발하는 동안에 큰 도움이 됩니다. 단위 테스트는 일반적으로 다음과 같은 내용을 확인합니다. 일반적인 유스 케이스: 요소가 사용될 거라고 예상되는 일반적인 방법을 테스트한다. 일반적인 오류 케이스와 잠재적인 문제: 제대로 동작하지 않을 거라고 예상되는 일반적인 부분, 과거에 문제가 발생했던 부분 등을 테스트한다. 에지 케이스와 잘못된 아규먼트: Int의 경우 Int.MAX_VALUE를 사용하는 경우, nullable의 경우 'nul..
close 메서드를 사용해서 명시적으로 닫아야 하는 리소스가 있습니다. InputStream과 OutputStream java.sql.Connection java.io.Reader(FileReader, BufferedReader, CSSParser) java.new.Socket과 java.util.Scanner 이러한 리소스들은 AutoCloseable을 상속받는 Closeable 인터페이스를 구현하고 있습니다. // Closeable public interface Closeable extends AutoCloseable { void close() throws IOException; } // AutoCloseable public interface AutoCloseable { void close() thr..
프로퍼티가 null이라는 것은 값이 설정되지 않았거나, 제거되었다는 것을 나타냅니다. 함수가 null을 리턴한다는 것은 함수에 따라서 여러 의미를 가질 수 있습니다. String.toIntOrNull()은 String을 Int로 적절하게 변환할 수 없을 경우 null을 리턴한다. Iterable.firstOrNull(() -> Boolean)은 주어진 조건에 맞는 요소가 없을 경우 null을 리턴한다. 기본적으로 nullable 타입은 세 가지 방법으로 처리합니다. ?. , 스마트 캐스팅, Elvis 연산자 등을 활용해서 안전하게 처리한다. 오류를 throw한다. 함수 또는 프로퍼티를 리펙터링해서 nullable 타입이 나오지 않게 바꾼다. null을 안전하게 처리하기 null을 안전하게 처리하는 방법 ..