티스토리 뷰
반응형
[Kotlin in Action - http://www.yes24.com/Product/Goods/55148593]
3.1 코틀린에서 컬렉션 만들기
- 코틀린은 자신만의 컬렉션 기능을 제공하지 않는다
- 자바 개발자가 기존 자바 컬렉션을 활용할 수 있다
- 표준 자바 컬렉션을 활용하면 자바 코드와 상호 작용하기가 더 쉽다
- 코틀린 컬렉션은 자바 컬렉션과 똑같은 클래스이지만 자바보다 더 많은 기능을 쓸 수 있다
3.2 함수를 호출하기 쉽게 만들기
- 함수가 제네릭(generic)하다 -> 이 함수는 어떤 타입의 값을 원소로하든 처리할 수 있다
3.2.1 이름 붙인 인자
- 코틀린으로 작성한 함수를 호출할 때는 함수에 전달하는 인자 중 일부(또는 전부)의 이름을 명시할 수 있다
- 호출 시 인자 어느 하나라도 이름을 명시하고 나면 혼동을 막기 위해 그 뒤에 오는 모든 인자는 이름을 꼭 명시해야 한다
- 자바로 작성한 코드를 호출할 때는 이름 붙인 인자를 사용할 수 없다
3.2.2 디폴트 파라미터 값
- 코틀린에서는 함수 선언에서 파라미터의 디폴트 값을 지정할 수 있다
- 함수 오버로드 중 상당수를 피할 수 있다
- 일반 호출 문법을 사용하려면 함수를 선언할 때와 같은 순서로 인자를 지정해야 한다
- 이름 붙은 인자를 사용하는 경우에는 인자 목록의 중간에 있는 인자를 생략하고, 지정하고 싶은 인자를 이름을 붙어서 순서와 관계없이 지정할 수 있다
- 함수의 디폴트 파라미터 값은 함수를 호출하는 쪽이 아니라 함수 선언 쪽에서 지정된다
- 디폴트 값과 자바
- 자바에는 디폴트 파라미터 값이라는 개념이 없어서 코틀린 함수를 자바에서 호출하는 경우에는 그 코틀린 함수가 디폴트 파라미터 값을 제공하더라도 모든 인자를 명시해야 한다
- @JvmOverloads 애노테이션을 함수에 추가하면 코틀린 컴파일러가 자동으로 맨 마지막 파라미터로부터 파라미터를 하나씩 생략한 오버로딩한 자바 메소드를 추가해준다
3.2.3 정적인 유틸리티 클래스 없애기: 최상위 함수와 프로퍼티
- 코틀린에서는 함수를 클래스 안에 선언할 필요가 전혀 없다
- JVM은 클래스 안에 들어있는 코드만을 실행할 수 있기 때문에 컴파일러는 이 파일을 컴파일할 때 새로운 클래스를 정의해준다
- 코틀린 컴파일러가 생성하는 클래스의 이름은 최상위 함수가 들어있던 코틀린 소스 파일의 이름과 대응한다
코틀린 파일의 모든 최상위 함수는 이 클래스의 정적인 메소드가 된다
ex) Join.kt의 최상위 함수 joinToString() ---컴파일---> JoinKt클래스의 정적 멤버 함수 joinToString()
- 코틀린 컴파일러가 생성하는 클래스의 이름은 최상위 함수가 들어있던 코틀린 소스 파일의 이름과 대응한다
- 파일에 대응하는 클래스의 이름 변경하기
- 파일의 맨 앞, 패키지 이름 선언 이전에 @JvmName 애노테이션으로 지정할 수 있다
- 최상위 프로퍼티
- 프로퍼티도 파일의 최상위 수준에 놓을 수 있다
- 기본적으로 최상위 프로퍼티도 다른 모든 프로퍼티처럼 접근자 메소드를 통해 자바 코드에 노출된다
-> val의 경우 게터, var의 경우 게터와 세터가 생긴다 - const 변경자를 추가하면 프로퍼티를 public final static 필드로 컴파일하게 만들 수 있다
3.3 메소드를 다른 클래스에 추가: 확장 함수와 확장 프로퍼티
- 확장 함수(extension function) : 어떤 클래스의 멤버 메소드인 것처럼 호출할 수 있지만 그 클래스의 밖에 선언된 함수
- 수신 객체 타입(receiver type) : 확장이 정의될 클래스의 타입
- 수신 객체(receiver object) : 확장 함수가 호출되는 대상이 되는 값, 수신 객체 타입(클래스)에 속한 인스턴스 객체
- 자바 클래스로 컴파일한 클래스 파일이 있는 한 그 클래스에 원하는 대로 확장을 추가할 수 있다
- 확장 함수 본문에도 this를 쓸 수 있다
- 확장 함수 본문에서도 this를 생략할 수 있다
- 확장 함수 안에서는 클래스 내부에서만 사용할 수 있는 비공개(private) 멤버나 보호된(protected) 멤버를 사용할 수 없다
- 확장 함수 내부에서 수신 객체의 멤버 메소드와 확장 함수를 모두 호출할 수 있다
- 호출하는 쪽에서는 확장 함수와 멤버 메소드를 구분할 수 없다
- 호출한 메소드가 확장 함수인지 멤버 메소드인지 여부가 중요한 경우도 거의 없다
3.3.1 임포트와 확장 함수
- 코틀린에서는 클래스를 임포트할 때와 동일한 구문을 사용해 개별 함수를 임포트할 수 있다
- *, as 키워드도 사용할 수 있다
- 한 파일 안에서 다른 여러 패키지에 속해있는 이름이 같은 함수를 가져와 사용해야 하는 경우 이름을 바꿔서 임포트하면 이름 충돌을 막을 수 있다
- 코틀린 문법상 확장 함수는 반드시 짧은 이름을 써야 한다
- 임포트할 때 이름을 바꾸는 것이 확장 함수 이름 충돌을 해결할 수 있는 유일한 방법이다
3.3.2 자바에서 확장 함수 호출
- 내부적으로 확장 함수는 수신 객체를 첫 번째 인자로 받는 정적 메소드다
- 확장 함수가 들어있는 자바 클래스 이름도 확장 함수가 들어있는 파일 이름에 따라 결정된다
3.3.3 확장 함수로 유틸리티 함수 정의
- 확장 함수는 단지 정적 메소드 호출에 대한 문법적인 편의(syntatic sugar)일 뿐이다
- 클래스가 아닌 더 구체적인 타입을 수신 객체 타입으로 지정할 수동 있다
- 확장 함수를 하위 클래스에서 오버라이드할 수는 없다
3.3.4 확장 함수는 오버라이드할 수 없다
- 확장 함수는 오버라이드 할 수 없다
- 동적 디스패치(dynamic dispatch) : 실행 시점에 객체 타입에 따라 동적으로 호출될 대상 메소드를 결정하는 방식
정적 디스패치(static dispatch) : 컴파일 시점에 알려진 변수 타입에 따라 정해진 메소드를 호출하는 방식 - 확장 함수는 클래스의 일부가 아니다. 확장 함수는 클래스 밖에 선언된다
- 확장 함수를 호출할 때 수신 객체로 지정한 변수의 정적 타입에 의해 어떤 확장함수가 호출 될지 결정되지,
그 변수에 저장된 객체의 동적인 타입에 의해 확장 함수가 결정되지 않는다
- 확장 함수를 호출할 때 수신 객체로 지정한 변수의 정적 타입에 의해 어떤 확장함수가 호출 될지 결정되지,
open class View {
open fun click() = println("View clicked!")
}
class Button: View() {
override fun click() = println("Button clicked!")
}
fun View.showOff() = println("I'm a view!")
fun Button.showOff() = println("I'm a button!")
fun main() {
val view: View = Button()
view.click() // Button clicked! <- "view"에 저장된 값의 실제 타입에 따라 호출할 메소드가 결정됨
view.showOff() // I'm a view! <- 확장함수는 정적으로 결정된다
}
- 확장 함수를 첫 번째 인자가 수신 객체인 정적 자바 메소드로 컴파일한다
3.3.5 확장 프로퍼티
- 확장 프로퍼티를 사용하면 기존 클래스 객체에 대한 프로퍼티 형식의 구문으로 사용할 수 있는 API를 추가할 수 있다
- 확장 프로퍼티는 아무 상태도 가질 수 없다
- 뒷받침하는 필드가 없어서 기본 게터 구현을 제공할 수 없으므로 최소한 게터는 꼭 정의를 해야 한다
- 초기화 코드에서 계산한 값을 담을 장소가 전혀 없으므로 초기화 코드도 쓸 수 없다
val String.lastChar: Char
get() = get(length - 1)
var StringBuilder.lastChar: Char
get() = get(length - 1)
set(value: Char) {
this.setCharAt(length - 1, value)
}
- 자바에서 확장 프로퍼티를 사용하고 싶다면 항상 게터나 세터를 명시적으로 호출해야 한다
3.4 컬렉션 처리: 가변 길이 인자, 중위 함수 호출, 라이브러리 지원
- vararg 키워드를 사용하면 호출 시 인자 개수가 달라질 수 있는 함수를 정의할 수 있다
- 중위(infix) 함수 호출 구문을 사용하면 인자가 하나뿐인 메소드를 간편하게 호출할 수 있다
- 구조 분해 선언(destructuring declaration)을 사용하면 복합적인 값을 분해해서 여러 변수에 나눠 담을 수 있다
3.4.1 자바 컬렉션 API 확장
3.4.2 가변 인자 함수: 인자 개수가 달라질 수 있는 함수 정의
- 가변 길이 인자 : 메소드를 호출할 때 원하는 개수만큼 값을 인자로 넘기면 자바 컴파일러가 배열에 그 값들을 얺어주는 기능
- 코틀린에서는 파라미터 앞에 vararg 변경자를 붙인다
- 이미 배열에 들어있는 원소를 가변 길이 인자로 넘길 때
자바에서는 배열을 그냥 넘기면 되지만
코틀린에서는 배열을 명시적으로 풀어서 배열의 각 원소가 인자로 전달되게 해야 한다- 스프레드 연산자(*)를 배열 앞에 붙이면 된다
3.4.3 값의 쌍 다루기: 중위 호출과 구조 분해 선언
- 중위 호출(infix call)
- 중위 호출 시에는 수신 객체와 유일한 메소드 인자 사이에 메소드 이름을 넣는다
- 인자가 하나뿐인 일반 메소드나 인자가 하나뿐인 확장 함수에 중위 호출을 사용할 수 있다
- 함수를 중위 호출에 사용하게 허용하고 싶으면 infix 변경자를 함수 선언 앞에 추가해야 한다
- 구조 분해 선언(destructuring declaration)
// 구조 분해 선언 예시
val (number, name) = 1 to "one"
for ((index, element) in collection.withIndex()) {
println("$index: $element")
}
3.5 문자열과 정규식 다루기
- 코틀린의 문자열은 자바 문자열과 같다
- 코틀린은 다양한 확장 함수를 제공함으로써 표준 자바 문자열을 더 즐겁게 다루게 해준다
- 혼동이 야기될 수 있는 일부 메소드에 대해 더 명확한 코틀린 확장 함수를 제공함으로써 프로그래머의 실수를 줄여준다
3.5.1 문자열 나누기
- 자바의 split의 구분 문자열은 실제로는 정규식(regular expression)이다
- 마침표(.)는 모든 문자를 나타내는 정규식으로 해석된다
- 코틀린에서는 split 함수에 전달하는 값의 타입에 따라 정규식이나 일반 ㅔㄱ스트 중 어니 것으로 문자열을 분리하는지 쉽게 알 수 있다
// 자바의 경우
"12.345-6.A".split(".") // 정규식으로 해석되서 .은 모든 문자열을 의미
// 결과 : 12.345-6.A
// 코틀린의 경우
"12.345-6.A".split("\\.|-".toRegex()) // 정규식을 명시적으로 전달
// 결과 : "12", "345", "6", "A"
"12.345-6.A".split(".", "-") // 여러 구분 문자열을 지정
// 결과 : "12", "345", "6", "A"
3.5.2 정규식과 3중 따옴표로 묶은 문자열
- 코틀린에서는 정규식을 사용하지 않고도 문자열을 쉽게 파싱할 수 있다
- substringBeforeLast(), substringAfterLast() 등 활용
- 3중 따옴표 문자열
- 3중 따옴표 문자열에서는 역슬래시(\)를 포함한 어떤 문자도 이스케이프할 필요가 없다
3.5.3 여러 줄 3중 따옴표 문자열
- 3중 따옴표 문자열에는 줄 바꿈을 표현하는 아무 문자열이나(이스케이프 없이) 그대로 들어간다
- 들여쓰기나 줄 바꿈을 포함한 모든 문자가 들어가기 때문에 여러 줄 문자열을 코드에서 더 보기 좋게 표현하고싶다면 들여쓰기를 하되 들여쓰기의 끝부분을 특별한 문자열로 표시하고 trimMargin을 사용해 그 문자열과 그 직전의 공백을 제거한다
- 여러 줄 문자열에는 줄 바꿈이 들어가지만 줄 바꿈을 \n과 같은 특수 문자를 사용해 넣을 수는 없다
- \를 문자열에 넣고 싶으면 이스케이프할 필요가 없다
- 3중 따옴표 문자열 안에 문자열 템플릿을 사용할 수도 있다
- 3중 따옴표 문자열 안에서는 이스케이프를 할 수 없기 때문에 $를 넣고 싶으면
"""${'$'}99.9"""처럼 문자열 템플릿 안에 '$'를 넣어야 한다
- 3중 따옴표 문자열 안에서는 이스케이프를 할 수 없기 때문에 $를 넣고 싶으면
- 라이브러리 알선(Pimp My Library)
- 기존 라이브러리를 새 언어에서 활용하는 패턴
3.6 코드 다듬기: 로컬 함수와 확장
- 코틀린에서는 함수에서 추출한 함수를 원 함수 내부에 중첩시킬 수 있다
- 로컬 함수는 자신이 속한 바깥 함수의 모든 파라미터와 변수를 사용할 수 있다
- 한 객체만을 다루면서 객체의 비공개 데이터를 다룰 필요는 없는 함수는 확장 함수로 만들면 객체.멤버처럼 수신 객체를 지정하지 않고도 공개된 멤버 프로퍼티나 메소드에 접근할 수 있다
- 일반적으로는 한 단계만 함수를 중첩시키라고 권장한다
3.7 요약
- 코틀린은 자체 컬렉션 클래스를 정의하지 않지만 자바 클래스를 확장해서 더 풍부한 API를 제공한다.
- 함수 파라미터의 디폴트 값을 정의하면 오버로딩한 함수를 정의할 필요성이 줄어든다. 이름붙인 인자를 사용하면 함수의 인자가 많을 때 함수 호출의 가독성을 더 향상시킬 수 있다.
- 코틀린 파일에서 클래스 멤버가 아닌 최상위 함수와 프로퍼티를 직접 선언할 수 있다. 이를 활용하면 코드 구조를 더 유연하게 만들 수 있다.
- 확장 함수와 프로퍼티를 사용하면 외부 라이브러리에 정의된 클래스를 포함해 모든 클래스의 API를 그 클래스의 소스코드를 바꿀 필요 없이 확장할 수 있다. 확장 함수를 사용해도 실행 시점에 부가 비용이 들지 않는다.
- 중위 호출을 통해 인자가 하나 밖에 없는 메소드나 확장 함수를 더 깔끔한 구문으로 호출할 수 있다.
- 코틀린읜 정규식과 일반 문자열을 처리할 때 유용한 다양한 문자열 처리 함수를 제공한다.
- 자바 문자열로 표현하려면 수많은 이스케이프가 필요한 문자열의 경우 3중 따옴표 문자열을 사용하면 더 깔끔하게 표현할 수 있다.
- 로컬 함수를 써서 코드를 더 깔끔하게 유지하면서 중복을 제거할 수 있다.
반응형
'study > kotlin in action' 카테고리의 다른 글
부록E. 코루틴과 Async/Await (0) | 2022.04.18 |
---|---|
2. 코틀린 기초 (0) | 2022.01.01 |
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- 도커
- cacheable
- QuickTimePlayer
- 자바스크립트
- gasmask
- ddd
- docker
- JavaScript
- gradle
- 코틀린
- linuxkit
- 스프링
- 쿠버네티스
- docker pull limit
- 클린코드
- kotlin In Action
- docker for mac
- kotlin
- IntelliJ
- 스프링부트
- back merge
- 도메인주도설계
- clean code
- springboot
- k8s
- 자바
- java
- ImagePullBackOff
- Kubernetes
- Spring
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
글 보관함