코틀린 (Kotlin) 유용한 도구 및 확장함수, 확장 프로퍼티

sealed class 사용 sealed class Result< out T : Any> { data class Success< out T : Any>( val data : T ) : Result< T >() sealed class Error( val exception : Exception) : Result<Nothing>() { class RecoverableError(exception: Exception) : Error(exception) class NonRecoverableError(exception: Exception) : Error(exception) } object InProgress : Result<Nothing>() } sealed class 는 동일한 파일 또는 중첩 클래스로 정의해야 한다. sealed class 인 Result 클래스는 private 생성자로 생성된다. sealed class 인 Error 클래스는 private 생성자로 접근할 수 없다. val r : Result<Int> = Result.Error(Exception("error")) //컴파일 에러 발생 컴파일러만이 사용할 수 있는 생성자를 생성한다. InProgress 는 저장할 상태가 없기때문에 불필요한 할당을 피하기위해서 싱글톤으로 생성했다.(object) val result = when (result) { is Result.Success -> TODO () is Result.Error.RecoverableError -> TODO () is Result.Error.NonRecoverableError -> TODO () is Result.InProgress -> TODO () } when 구문을 사용했을 때 sealed clas

코틀린 (Kotlin) 컬렉션

코틀린 컬렉션 fun functionalFilter(numList : List<Int>) : List<Int> = numList.filter { it % 2 == 0 } 코드 간결성/가독성 향상 결과값을 저장하기 위한 별도의 리스트를 생성할 필요가 없다 비즈니스 로직에 집중 버그가 발생할 확률이 적다 테스트가 용이 유지보수가 용이 fun functionalMap(numList: List<Int>) : List<Int> {     return numList.map { it + 2 } } map 함수는 내부적으로 새로운 리스트를 만들어서 반환 -> 부수효과 없음 tailrec fun<T, R> FunList<T>.map(acc: FunList<R> = FunList.Nil, f: (T) -> R): FunList<R> = when (this) {     FunList.Nil -> acc.reverse()     is FunList.Cons -> tail.map(acc.addHead(f(head)), f) } 제네릭과 고차함수를 활용해서 일반화된 함수를 만들고, 재사용성을 높이는 것이 함수적 접근 방식 명령형과 함수형의 차이 fun func(intList: List<Int>) =     intList         .map { n ->             println("map=$n")             n*n }         .filter { n ->             println("filter=$n")             n < 10 }         .first() println(func(listOf(1,2,3,4,5))) //"1"출력 map 5번 실행

코틀린 (Kotlin) 고차함수

코틀린 고차함수 재사용성     val sum: (Int, Int) -> Int = { x, y -> x + y }     val product: (Int, Int) -> Int = { x, y -> x * y }     val minus: (Int, Int) -> Int = { x, y -> x - y }         println(higherOrder(sum, 1, 5))     // 6     println(higherOrder(minus, 5, 2))   // 3     println(higherOrder(product, 4, 2)) // 8 기능확장성     val twiceSum: (Int, Int) -> Int = { x, y -> (x + y) * 2 }     println(higherOrder(twiceSum, 8, 2))   // 20 간결성 map, filter 로 간결하고 가독성을 향상시킴     val result2 = ints             .map { it * 2 }             .filter { it > 10 }     println(result2)      부분 함수 특정한 값이나 범위내에 있을 때만 함수가 동작하도록 할수 있음 호출자가 함수가 던지는 예외나 오류값에 대해서 몰라도 된다 부분 함수의 조합으로 부분 함수 자체를 재사용할 수도있고, 확장할 수도 있다 class PartialFunction <in P, out R> (     private val condition: (P) -> Boolean,     private val f: (P) -> R)     : (P) -> R {     override fun invoke(p: P): R = when {         condition(p)

코틀린 (Kotlin) 함수형 프로그래밍

함수형 프로그래밍 불변성(immutable) 참조 투명성(referential transparency) 일급 함수(first-class-function) 게으른 평가(lazy evaluation) 순수한 함수 동일한 입력->동일한 결과 부수효과 없음 부수효과... 전역 변수, 객체 상태변경 파일, 네트워크 출력 예외발생 일급 함수 함수를 함수 매개변수로 사용 함수를 반환값으로 사용 함수를 변수에 저장 가능 게으른 평가 val lazy: String by lazy {    //long work... } print(lazy) print(lazy) 값을 사용할 때 lazy{...} 작업이 실행되어 할당된다. 코드가 실행될때 할당되지 않기때문에 오래걸리는 작업을 필요할때 실행하여 작업을 효율적으로 동작가능. val 로만 가능하며 최초 한번만 할당되고 두번째부터는 값이 재사용된다. 시퀀스(sequence) val infinite = generatieSequence(0) { it + 10 } infinite.take(10).forEach {println("$it")} 실제 사용할때 사용되기때문에 무한대 자료구조로서 사용할 수 있다. Unit public object Unit { override fun toString() = "kotlin.Unit" } Unit 은 자바의 void 처럼 아무것도 반환하지 않을때 사용. Unit 은 참조형 타입이다. 확장함수 private fun String.addHelloPrefix(): String = "Hello, ${this}" if 조건 표현식 조건문을 구문과 표현식으로 사용할 수 있다. 표현식으로 사용된 경우 else 가 필요하며 변수에 할당할 수 있다. val x: Int

코틀린 (Kotlin) with() apply() 함수 정리

with 함수 객체 이름을 반복하지 않고 사용하고 싶을 때 with 함수를 사용하면 편리합니다. val stringBuilder = StringBuilder() val text = with ( stringBuilder ) { //수신객체 지정     for (letter in 'A'..'Z') {         append(letter) //바로 사용가능, this.append(letter) 와 동일     }     toString() //블럭마지막은 리턴값 } 만약 with 블럭 안에서 함수 이름이 같은 바깥 함수를 호출하고 싶을 경우에는 this@OuterClass.함수() 처럼 호출하면 됩니다. val dup = with (stringBuilder) {     //바깥 클래스와 이름이 중첩되었고     //바깥 클래스 함수를 호출하고 싶다면 this@outerclass.함수() 한다     this @RamdaActivity .toString() } apply 함수 apply 함수는 with 와 비슷하지만 자기 자신을 리턴합니다. 객체를 생성하면서 속성을 초기화할 때 사용됩니다. 그리고 자바의 빌더 객체와 비슷한 기능을 따라할 수 있습니다. //자신에게 전달된 수신객체를 리턴한다 val text = StringBuilder(). apply {     for (letter in 'A'..'Z') {         append (letter) //바로 사용가능     } }. toString () //리턴된 StringBuilder의 toString() 을 호출 //StringBuilder 를 편리하게 사용 가능 //buildString 은 표준라이브러리로 StringBuilder와 toString() 을 호출해주는 함수 val text = buildString {     for (letter in 'A'..&

코틀린 (Kotlin) 시퀀스 사용하기(지연 계산)

지연 계산 아래 코드는 map과 filter 를 호출합니다. 그러나 map 에서 리스트를 생성하고 filter 에서도 리스트를 새로 생성하기때문에 데이터가 많은 경우 효율적이지 않습니다. people. map (Person::name). filter { it.startsWith("A") } 시퀀스(sequence) 큰 데이터를 연산할 때는 중간 리스트를 생성하지 않는 시퀀스를 사용하는 것이 효율적입니다. 시퀀스는 아래 연산처럼 중간 연산에서는 아무것도 실행되지 않습니다. listOf(1, 2, 3, 4)             . asSequence ()             . map { it * it }             . filter { it % 2 == 0 } 시퀀스는 최종 연산이 호출될 때 실행됩니다. listOf(1, 2, 3, 4)     . asSequence ()     . map { it * it }     . filter { it % 2 == 0 }     . toList () //최종 연산 시퀀스의 실행 시퀀스의 연산은 각 값에 대해 순차적으로 적용됩니다. listOf(1, 2, 3, 4)     . asSequence ()     . map { it * it }     . find { it > 3 } 위 코드의 경우 실행 순서는 다음과 같습니다. list(1) -> map (1*1) -> find (1>3) -> list(2) -> map (2*2) -> find (4>3) -> 종료 이처럼 시퀀스는 중간 리스트를 만들지않고 값을 순차적으로 계산하기때문에 특정 조건이 만족하는경우 중간에 이터레이션을 종료할 수 있습니다. 시퀀스가 없는 경우에는 map 리스트(1,4,9,10) 을 생성하고 map 리스트 값에서 find 로 4를 찾아 반환할 것이기 때문에 불필요한 중간 리스트가 생성됩니다

코틀린 (Kotlin) filter, map, all, any, count, find, groupBy, flatMap 함수 정리

filter 함수 filter 함수는 리스트를 이터레이션하면서 리턴이 true 인 값만 필터링합니다. var listInts = listOf(1,2,3,4) //filter listInts. filter { it % 2 == 0 } //출력 [2,4] map 함수 map 함수는 값을 변형해서 새로운 리스트를 생성합니다. var listInts = listOf(1,2,3,4) listInts. map { it * it } //출력 [1,4,9,16] val people = listOf(Person("A", 27), Person("B", 35), Person("C", 35)) listInts. map { it.name } //출력 [A, B, C] //심플한 방법 people. map (Person::name) //30살이상의 이름 출력 people. filter { it.age >= 30 }. map (Person::name) //가장 나이가 많은 사람들 모두 출력하기 //1. 최대값을 구하는 반복작업이 쓸데없이 많은 코드 people. filter { it.age == (people.maxBy(Person::age)!!.age) } //매번 maxBy 호출 //2. 계산이 중복되는것을 피하자. 람다안에 람다를 넣는것은 내부 로직을 매우 복잡하게 할 수 있음 val maxAge = people. maxBy (Person::age)!!.age //외부로 빼서 불필요한 반복 제거 people. filter { it.age == maxAge } //맵 사용 //map 에는 filterKeys, mapKeys 와 filterValues, mapValues 함수가 있음 val numbers = mapOf (0 to "zero", 1 to "one") val upperNumbers = numbers.ma

코틀린 (Kotlin) 람다 문법

코틀린 람다 문법 코틀린 람다는 자바의 람다와 비슷하지만 문법이 조금 다릅니다. { x: Int, y:Int -> x+y} 처럼 블럭({..}) 안에 파라메터와 -> 를 구분자로해서 본문을 작성할 수 있습니다. 그리고 람다를 변수에 저장할 수 있습니다. val sum = { x: Int, y:Int -> x+y} //람다식을 변수에 저장 //사용 sum(1,2) //풀어쓴 람다 people. maxBy ({ p : Person -> p.age}) //맨 뒤에 있는 인자가 람다 식인경우 밖으로 뺄 수 있다 people. maxBy () {     p : Person -> p.age } //람다가 유일한 인자인경우 괄호 생략 가능 people. maxBy {     p : Person -> p.age } 아래 코드는 파라메터가 두개 이상이고 마지막 하나만 람다인 경우입니다. people. joinToString (" ") { p: Person -> p.name } //마지막 람다를 블럭밖으로 뺌 람다 파라미터에서 타입을 제거할 수 있습니다. //파라미터 타입 명시 people. maxBy { p:Person -> p.age } //파라미터 타입 추론(컴파일러가 추론) people. maxBy { p -> p.age } 람다의 파라미터가 하나이고 컴파일러가 타입을 추론할 수 있는 경우 it 을 사용 할 수 있습니다. //it 사용 : 람다의 파라미터가 하나인경우 컴파일러가 추론 가능 //중첩된 람다에서는 it 보다는 파라미터를 명시하는 것이 좋다 people. maxBy { it .age } 람다를 변수에 저장할 때는 타입을 추론할 수 없기때문에 타입을 명시해야 됩니다. //람다를 변수에 저장할때는 추론할수 없기때문에 파라미터를 명시한다 val getAge = { p:Person -> p.age }

코틀린 (Kotlin) object 키워드와 동반 객체 정리

코틀린 obeject 키워드 object 는 흔히 자바나 안드로이드에서 사용하는 무명 내부 클래스(anonymous inner class) 처럼 사용할 수 있습니다. object 키워드는 클래스를 정의하면서 객체를 생성 합니다. 코틀린 싱글턴 만들기 코틀린은 object 키워드를 이용해서 별다른 코드없이 싱글턴 구현을 지원합니다. 자바처럼 클래스를 만들고 static 객체로 한번만 할당해주는 코드가 object Payroll 이라고 클래스를 생성해 주기만하면 static 객체에 할당해주는 것을 자동으로 생성해줍니다. object Payroll { //object 클래스 생성으로 싱글턴을 구현     val allEmplyees = arrayListOf<Person> ()     fun calculateSalary() {         //...     } } //사용 Payroll.calculateSalary() //싱글턴처럼 . 으로 접근 Payroll.allEmplyees. add(Person( "zerog" , Company( "zerog" , Address( "" )))) object 생성자 object 객체는 주 생성자와 부 생성자 모두 사용할 수 없습니다. //ERROR!!! object 는 생성자를 사용할 수 없음 object Payroll(val a:String) {    ... } object 상속 object 객체는 상속이 가능합니다. 예를들어 멤버변수로 상태값을 저장하지않고 메소드만 override 하여 사용하는 클래스의 경우 object 로 상속을 받아 생성자없이 바로 사용가능하도록 만들수 있습니다. //object 로 구현한 경우 object CaseInsensitiveFileComparator: Comparator {     override fun compare(o1: File?, o2: F

코틀린 (Kotlin) by 키워드 정리

코틀린 by 키워드 개발하다 보면 하위 클래스가 상위 클래스를 상속해서 상위 클래스 메소드를 오버라이드를 하는 경우가 많습니다. 이러한 상황에서 유지보수를 하다보면 상위 클래스가 변경이되는 경우 하위 클래스가 상위 클래스에 의존하고 있던 상황이 변경되면서 예기치 않은 오류가 발생합니다. 그래서 코틀린에서는 기본적으로 클래스는 final 입니다. 상속가능한 클래스는 open 을 해서 상속이 가능하다는 것을 알려줍니다. 상위 클래스 변경 시 하위 클래스에 영향을 줄 수 있다는 것을 인지시켜 줄수 있습니다. by - 위임 상속을 허용하지 않는 클래스에 새로운 기능을 추가 할때는 위임을 사용할 수 있습니다. 위임을 사용하면 상속하지 않고 기존 기능을 그대로 사용하면서 새로운 기능을 추가 할 수 있습니다. 코틀린에서는 by 키워드 를 사용하여 위임 기능을 사용할 수 있습니다. //상속하지않고 새로운 함수 추가하기 class CountingSet<T> (     val innerSet : MutableCollection <T> = HashSet () ) : MutableCollection <T> by innerSet { //MutableCollection 구현을 innerSet 에 위임      var objectsAdded = 0 //카운트     //override 함수는 HashSet() 대신 구현한 것을 사용      override fun add(element: T): Boolean {           objectsAdded ++    //add할때마다 카운트 증가           return innerSet.add(element)     }     //override 함수는 HashSet() 대신 구현한 것을 사용      override fun addAll(c: Collection<T> ): Boolean {           objectsAdde

코틀린 (Kotlin) 데이터 클래스 정리

데이터 클래스 data class 는 toString , equals , hashcode 함수를 자동 생성 해 줍니다. 그래서 자바와 다르게 코틀린에서 == 은 equals 를 호출합니다. 자바처럼 참조를 호출하려면 === 을 사용합니다. data class Client( val name : String, val code : String) 데이터 클래스 - copy() 함수 제공 데이터 클래스는 copy() 함수를 제공합니다. 코틀린에서 프로퍼티는 val 또는 var 도 가능합니다. 그러나 데이터 클래스의 모든 프로퍼티를 읽기 전용(val)으로 만들어 불변 클래스로 만들기를 권장합니다. 다중 스레드일때 값이 변경되어 동기화할 필요가 줄어들도록 하기 위해서입니다. 객체를 메모리에서 값을 변경하는 것보다 복사본을 만드는 것이 더 좋습니다. public final class Client {     public final Client copy (@NotNull String name, @NotNull String code) {         return new Client(name, code)  //새로운 객체를 생성해서 복사     } } //사용 val client = Client( "zerog" , "1111" ) val clientCopy = client. copy ( "zerog2" , "2222" ) //복사 *개인적으로 코틀린을 공부하면서 정리한 자료입니다. 수정 사항 및 이슈가 있는 경우 메일 부탁드립니다.

코틀린 (Kotlin) 생성자 정리

주 생성자와 초기화 블록 코틀린의 주 생성자는 클래스 이름 뒤에 괄호로 둘러쌓여 있습니다. open class User constructor (_nicname:String) { // 주생성자     val nickname: String     init {         //주생성자와 함께 초기화 블럭         //init 초기화 블록은 여러개 가능         nickname = _nicname     } } constructor 는 주 생성자 나 부 생성자 를 정의합니다. init 은 초기화 블럭 입니다. 이 초기화 블럭은 주 생성자와 함께 사용되며 init 은 여러개 사용할 수 있습니다. 초기화할 때 로직이 필요한 경우 init 초기화 블록내 로직을 추가하여 초기화 합니다. 조금 더 간단하게 생성자 생성하기 주 생성자 앞에 애노테이션이나 가시성 변경자가 없다면 constructor 는 생략 가능 합니다. 그리고 단순히 주 생성자의 파라미터로 초기화 해줄때는 init 도 생략 가능 합니다. class User ( _nicname:String ) { // 주생성자: constructor 생략 가능     //주 생성자의 파라미터로 초기화, 초기화가 없으면 프로퍼티로도 생성되지 않음,     //getNicname() 함수가 생성 됨     //생성자에서 this.nicname = _nicname 함     val nickname: String = _nicname } 최대한 간략하게 생성자 생성하기 위 코드보다 더 축약이 가능합니다. 주 생성자의 파라미터로 프로퍼티를 초기화하는 경우 생성자 파라미터에 var 또는 val 을 추가하여 간략하게 생성할 수 있습니다 //val 을 사용하면 프로퍼티가 생성 됨 //getNicname() 함수 생성 됨 //생성자에서  this.nicname = nicname 함 class User ( val nicname:String) { }

코틀린 (Kotlin) 내부 클래스 sealed class 정리

내부 클래스 자바에서 내부 클래스는 바깥쪽 클래스에 대해 참조 를 합니다. 그래서 static class 로 선언하여 바깥쪽 클래스에 대한 참조를 제거할 수 있습니다. 코틀린에서는 기본적으로 static 중첩 클래스 와 같습니다. 그리고 바깥쪽 클래스에 대한 참조를 갖고 싶으면 inner 변경자 를 붙입니다. class Button : View {     override fun getCurrentState(): State {         return ButtonState()     }     //내부 클래스     class ButtonState: State {         fun save() {             getCurrentState() //error! 바깥쪽 참조를 할 수 없다         }     }    //inner class 로 바깥쪽 클래스 참조 가능     inner class ButtonStateInner:State {         fun save() {             getCurrentState() //바깥쪽 클래스 참조 가능         }         fun getOuterReference() {             //this@클래스 로 접근 또는 생략가능             this@ Button.getCurrentState() //바깥쪽 클래스 참조 가능         }     } } sealed class 코틀린에서는 상위 클래스를 상속한 하위 클래스 정의를 제한 할 수 있습니다. sealed class Expr { //sealed class 는 open      class Num(val value:Int) : Expr()     class Sum(val left: Expr, val right: Expr) : Expr()     class Total() //when 에 추가를 하지않은 경우 컴파일 에러

코틀린 (Kotlin) open, final, abstract

코틀린 open 접근자 코틀린의 클래스는 디폴트로 final public 입니다. 그래서 상속을 하려면 open class 이여야 하며 open fun 이여야 합니다. open class RichButton : Clickable {     //interface 는 public 이기 때문에 override 가능     override fun click() {           }     //final 이기때문에 override 불가능     fun disable() {           }     //open 이기때문에 override 가능     open fun animate() {           } } RichButton 의 열려있는 animate() 를 상속한 하위 클래스에서 override 하면서 final 로 제한 할 수 있습니다. open class RichRichButton : RichButton() {     //추가로 final 을 붙이면 하위 클래스에서는 override 할 수 없다     final override fun click() {         super.click()     }     final override fun animate() {         super.animate()     }     final override fun showOff() {         super.showOff()     } } 코틀린 추상 클래스 코틀린의 추상클래스는 자바와 같습니다. 다만, 추상메소드가 아닌 메소드는 final 접근자 입니다. 추상 메소드와 open 을 명시적으로 추가한 메소드는  override 할 수 있습니다. abstract class Animated {     //추상 메소드는 열려있다     abstract fun animate()      //추상 클래스이더라도 추상 메소드가 아니면 final 이다     fu