목록Kotlin (85)
안드로이드 개발자 노트
해시 테이블은 각 요소에 숫자를 할당하는 함수가 필요합니다. 이 함수를 해시 함수라고 부르며, 같은 요소라면 항상 같은 숫자를 리턴합니다. 해시 함수는 빠르고 충돌이 적을 수록 좋습니다. 가변성과 관련된 문제 요소가 추가될 때만 해시 코드를 계산합니다. 요소가 변경되어도 해시 코드는 계산되지 않으며, 버킷 재배치도 이루어지지 않습니다. 그래서 기본적인 LinkedHashSet와 LinkedHashMap의 키는 한 번 추가한 요소를 변경할 수 없습니다. 세트와 맵의 키로 mutable 요소를 사용하면 안되고, 사용하더라도 요소를 변경해서는 안됩니다. data class FullName( var name: String, var surname: String ) val person = FullName("Maja..
코틀린에는 두 가지 종류의 동등성(equality)이 있습니다. 구조적 동등성(structural equality) equals 메서드와 이를 기반으로 만들어진 == 연산자(!= 포함)로 확인하는 동등성입니다. a == b 는 a가 nullable이 아니라면 a.eqauls(b), a가 nullable이라면 a?.equals(b) ?: (b === null)로 변환됩니다. 레퍼런스적 동등성(referential equality) === 연산자(!== 포함)로 확인하는 동등성입니다. 두 피연산자가 같은 객체를 가리키면, true를 리턴합니다. equals는 모든 클래스의 슈퍼클래스인 Any에 구현되어 있으므로, 모든 객체에서 사용할 수 있습니다. 서로 다른 타입의 두 객체를 비교하는 것은 허용되지 않으며,..
태그를 포함한 클래스를 태그 클래스(tagged class)라고 부릅니다. 상수(constant) '모드'를 태그라고 부르며, 태그 클래스는 서로 다른 책임을 한 클래스에 태그로 구분해서 넣는다는 문제를 내포합니다. 예를 들어 테스트에 사용되는 클래스로서 어떤 값이 기준에 만족하는지 확인하기 위해 사용되는 클래스를 살펴보겠습니다. class ValueMatcher private constructor( private val value: T? = null, private val matcher: Matcher ) { fun match(value: T?) = when(matcher) { Matcher.EQUAL -> value == this.value Matcher.NOT_EQUAL -> value != thi..
대부분의 프로그래밍 언어에는 함수 타입이 없어서 연산 또는 액션을 전달할 때 메서드가 하나만 있는 인터페이스를 활용합니다. 이러한 인터페이스를 SAM(Single-Abstract Method)이라고 부릅니다. 함수가 SAM을 받는다면, 이러한 인터페이스를 구현한 객체를 전달받는다는 의미입니다. 예를 들어 다음 코드는 뷰를 클릭했을 때 발생하는 정보를 전달하는 SAM입니다. interface OnClick { fun clicked(view: View) } fun setOnclickListener(listener: Onclick) { // ... } setOnClickListener(object: Onclick { override fun clicked(view: View) { // ... } }) 함수 타입..
때로는 데이터를 한꺼번에 전달해야 할 때가 있는데, 일반적으로 이러한 상황에서는 data 클래스를 사용합니다. data class Player( val id: Int, val name: String, val points: Int ) val player = Player(0, "Gecko", 9999) data 한정자를 붙이면, 몇 가지 함수가 자동으로 생성됩니다. toString: 클래스 이름과 기본 생성자 형태로 모든 프로퍼티와 값을 출력 equals: 기본 생성자의 프로퍼티가 같은지 확인 player == Player(0, "Gecko", 9999) // true player == Player(0, "Ross", 9999) // false hashCode: equals와 같은 결과를 낸다. copy: ..
상속은 객체 계층 구조를 만들기 위해 설계되었으며, 관계가 명확하지 않을 때 사용하면 여러 가지 문제가 발생할 수 있습니다. 단순하게 코드 추출 또는 재사용을 위해 상속을 하려고 한다면, 일반적으로 상속보다 컴포지션을 사용하는 것이 좋습니다. 간단한 행위 재사용 예시로 프로그레스 바를 어떤 로직 처리 전에 출력하고, 처리 후에 숨기는 동작을 하는 두 개의 클래스가 있다고 하겠습니다. class ProfileLoader { fun load() { // 프로그레스 바를 보여 줌 // 프로파일을 읽어 들임 // 프로그레스 바를 숨김 } } class ImageLoader { fun load() { // 프로그레스 바를 보여 줌 // 이미지를 읽어 들임 // 프로그레스 바를 숨김 } } 이러한 경우 많은 개발자..
코틀린은 DSL(Domain Specific Language)을 직접 만들 수 있습니다. DSL은 복잡한 객체, 계층 구조를 갖고 있는 객체들을 정의할 때 유용합니다. DSL을 만드는 것은 힘든 일이지만, 한 번 만들고 나면 보일러플레이트(boilerplate)와 복잡성을 숨기면서 개발자의 의도를 명확하게 표현할 수 있습니다. // HTML DSL body { div { a("https://kotlinlang.org") { target = ATarget.blank + "Main site" } } +"some content" } // Andriod View DSL (Anko 라이브러리) verticalLayout { val name = editText() button("Say Hello") { onClic..
객체를 정의하고 생성하는 가장 기본적인 방법은 기본 생성자(primary constructor)를 사용하는 것입니다. class user(var name: String, var surname: String) val user = User("Marcin", "Moskata") 기본 생성자로 객체를 만들 때는 객체의 초기상태를 나타내는 아규먼트를 전달합니다. 이러한 객체는 생성자로 상태를 초기화한 뒤, 그 프로퍼티를 유지합니다. data class Student( val name: String, val surname: String, val age: Int ) 점층적 생성자 패턴 점층적 생성자 패턴은 '여러 가지 종류의 생성자를 사용하는' 간단한 패턴입니다. class Pizza{ val size: String..
클래스의 인스턴스를 만드는 가장 일반적인 방법은 기본 생성자(primary constructor)를 사용하는 방법입니다. class MyLinkedList ( val head: T, val tail: MyLinkedList? ) val list = MyLinkedList(1, MyLinkedList(2, null)) 디자인 패턴으로 굉장히 다양한 생성 패턴들이 만들어져 있으며, 일반적으로 이러한 생성 패턴은 객체를 생성자로 생성하지 않고 별도의 함수를 통해 생성합니다. 생성자의 역할을 대신 해 주는 함수를 팩토리 함수라고 부르며, 다양한 장점들이 있습니다. 생성자와 다르게, 함수에 이름을 붙일 수 있다. 이름이 붙어있다면 훨씬 이해하기 쉽다. 생성자와 다르게, 함수가 원하는 형태의 타입을 리턴할 수 있다..
규약을 한쪽에서 위반할 수도 있습니다. 규약은 보증과 같으며, 규약을 위반하면 코드가 작동을 멈췄을 때 문제가 됩니다. 상속된 규약 클래스를 상속하거나, 다른 라이브러리의 인터페이스를 구현할 때는 규약을 반드시 지켜야 합니다. 규약을 지키지 않는다면, 객체가 제대로 동작하지 않을 수도 있습니다. 예를 들어 hashCode가 제대로 구현되지 않으면, HashSet과 함께 사용할 때 제대로 동작하지 않습니다. 다음 코드를 살펴보면, 원래 세트는 중복을 허용하지 않는데, equals가 제대로 구현되지 않았으므로 중복을 허용해 버립니다. class Id(val id: Int) { override fun equals(other: Any?) = other is Id && other.id == id } val set..