KotlinInAction 46

고차 함수 안에서 흐름 제어

람다를 둘러싼 함수로부터 반환(람다 안의 return문) data class Person(val name:String, val age: Int) val people = listOf(Person("Alice", 29), Person("Bob", 31)) fun lookForAlice(people: List){ for(person in people){ if(person.name == "Alice"){ println("Found!!") return } } println("Alice is not found") //people 안에 엘리스가 없으면 이 줄 출력 } 이 코드를 forEach로 바꿔 써도 될까? forEach에 넘긴 람다 안에 있는 return 도 위와 같은 의미이다 fun lookForAlice(p..

인라인 함수: 람다의 부가 비용 없애기

반복되는 코드를 별도의 라이브러리 함수로 빼내되 컴파일러가 자바의 일반 명령문만큼 효율적인 코드를 생성할게 만들 수는 없을까? 코틀린 컴파일러는 inline 변경자를 어떤 함수에 붙이면 컴파일러는 그 함수를 호출하는 모든 문장을 함수 본문에 해당하는 바이트 코드로 변경한다. 인라이닝이 작동하는 방식 어떤 함수를 inline으로 선언하면 그 함수의 본문이 인라인 된다. 함수를 호출하는 코드를 함수를 호출하는 바이트코드 대신에 함수 본문을 번역한 바이트 코드로 컴파일한다는 뜻이다. fun main(args: Array) { val lock = ReentrantLock() synchronized(lock){ // logic } } inline fun synchronized(lock: Lock, action: ..

고차 함수 정의

고차 함수는 다른 함수를 인자로 받거나 함수를 반환하는 함수이다. 코틀린에서는 람다나 함수 참조를 사용해 함수를 값으로 표현할 수 있다. 따라서 고차 함수는 람다나 함수 참조를 인자로 넘길 수 있거나, 람다나 함수 참조를 반환하는 함수이다. 함수 타입 람다를 인자로 받는 함수를 정의하려면 먼저 람다 인자의 타입을 어떻게 선언할 수 있는지 알아야 한다. fun main(args: Array) { //코틀린은 타입 추론으로 인해 변수 타입을 지정하지 않아도 된다. val sum = {x: Int, y: Int -> x + y} val action = {println(42)} } sum, action이 함수 타입임을 추론한다. fun main(args: Array) { val sum: (Int, Int) ->..

프로퍼티 접근자 로직 재활용: 위임 프로퍼티

위임 프로퍼티의 특성의 기반은 위임에 있다. 위임은 객체가 직접 작업을 수행하지 않고 다른 도우미 객체가 그 작업을 처리하게 맡기는 디자인 패턴을 말한다. 작업을 처리하는 도우미 객체를 위임 객체라고 부른다. 위임 프로퍼티 소개 class Foo { var p: Type by Delegate() } p 프로퍼티는 접근자 로직을 다른 객체에게 위임한다.(여기서는 Delegate 클래스의 인스턴스를 위임 객체로 사용) by 뒤에 있는 식을 계산해서 위임에 쓰일 객체를 얻는다. 프로퍼티 위임 객체가 따라야 하는 관례를 따르는 모든 객체를 위임에 사용할 수 있다. 디컴파일 class Foo{ private val delegate = Delegate() //컴파일러가 생성한 도우미 프로퍼티 var p: Type ..

구조 분해 선언과 component 함수

구조 분해를 사용하면 복합적인 값을 분해해서 여러 다른 변수를 한꺼번에 초기화할 수 있다. fun main(args: Array) { val p = Point(10, 20) val (x, y) = p //x,y 변수를 선언한 다음에 p의 여러 컴포넌트로 초기화한다. println(x) println(y) } 일반 변수 선언과 비슷하지만, =의 좌변에 여러 변수를 괄호로 묶었다는 점이 다르다 내부에서 구조 분해 선언은 관례를 사용한다 구조 분해 선언의 각 변수를 초기화하기 위해 componentN이라는 함수를 호출한다. N은 구조 분해 선언에 있는 변수 위치에 따라 붙는 번호이다. val x = p.component1(), val y = p.component2()로 변환된다 data 클래스의 주 생성자에 ..

컬렉션과 범위에 대해 쓸 수 있는 관례

컬렉션을 다룰 때 가장 많이 사용하는 연산은 인덱스를 사용해 원소를 읽거나 쓰는 연산과 어떤 값이 컬렉션에 속해있는지 검사하는 연산이다. 인덱스를 사용해 원소를 설정하거나 가져오고 싶다면 a [b]라는 식을 사용하고, 원소가 컬렉션이나 범위에 속하는지 검사하는 연산자로는 in 연산자를 사용한다. get과 set(인덱스로 원소에 접근) 코틀리에서 맵의 원소에 접근할 때나 자바에서 배열 원소에 접근할 때나 "[", "]"를 사용한다. 이 연산자를 사용해 변경 가능 맵에 키/값 쌍을 넣거나 이미 맵에 들어있는 키/값 연관 관계를 변경할 수 있다. multableMap[key] = newValue 코틀린에서는 인덱스 연산자도 관례를 따른다. 인덱스 연산자를 사용해 원소를 읽는 연산은 get 연산자 메서드로 변환..

비교 연산자 오버로딩

equals(동등성 연산자) 코틀린은 == 연산자 호출을 equals 메서드 호출로 컴파일한다. 사실 이는 특별한 경우가 아닌 여러 관례를 적용한 것에 불과하다 != 연산자를 사용하는 식도 equals 호출로 컴파일되며, 비교 결과를 뒤집은 값을 결과 값으로 사용한다. !=, == 모두 내부에서 인자가 널인지 검사하므로 다른 연산과 달리 널이 될 수 있는 값에도 적용할 수 있으며, 널이 아닐 경우에만 a.equals(b)를 호출하게 된다. class Point(val x: Int, val y: Int) { override fun equals(obj: Any?): Boolean {//Any에 정의된 메서드를 오버라이딩 if(obj === this) return true //최적화 : 파라미터가 this와 ..

산술 연산자 오버로딩

어떤 기능과 미리 정해진 이름의 함수를 연결해주는 기법을 코틀린에서는 관례라 부르고, 코틀린은 이 관례에 의존적이다. 이항 산술 연산 오버 로딩 data class Point(val x: Int, val y: Int){ operator fun plus(other: Point) : Point{ //plus라는 연산자 함수를 정의한다 return Point(x + other.x, y + other.y) //좌표를 성분별로 더한 새로운 점을 반환한다. } } fun main(args: Array) { val p1 = Point( 10, 20) val p2 = Point( 30, 40) println(p1 + p2) //+로 계산하면 plus 함수가 호출된다 } plus 함수 앞에 operator 키워드를 붙여..

컬렉션과 배열

널 가능성과 컬렉션 컬렉션 안에 널 값을 넣을 수 있는지 여부는 어떤 변수의 값이 널이 될 수 있는지 여부와 마찬가지로 중요하다. 변수 타입 뒤에 ?를 붙이면 그 변수에 널을 저장할 수 있다는 뜻인 것처럼 타입 인자로 쓰인 타입에도 같은 표시를 사용할 수 있다. fun readNumbers(reader: BufferedReader): List { val result = ArrayList() //널이 될 수 있는 Int 값으로 이뤄진 리스트 생성 for(line in reader.lineSequence()){ try{ val number = line.toInt() result.add(number) //정수를 리스트에 추가 }catch (e: NumberFormatException){ result.add(n..

코틀린의 원시 타입

원시 타입: Int, Boolean 등 자바의 원시 타입(int, double) 변수에는 그 값이 직접 들어가지만, 참조 타입(String)의 변수에는 메모리 상의 객체 위치가 들어간다. 원시 타입의 값을 더 효율적으로 저장하고, 여기저기 전달할 수 있지만, 메서드를 호출하거나 컬렉션에 원시 타입 값을 담을 수는 없다. 자바는 참조 타입이 필요한 경우 특별한 래퍼 타입으로 원시 타입을 감싸서 사용한다. fun main(args: Array) { val i:Int = 1 val list:List = listOf(1, 2, 3) } 코틀린은 원시 타입과 래퍼타입을 구분하지 않고 항상 같은 타입을 사용한다 fun main(args: Array) { showProgress(146) } fun showProgre..