티스토리 뷰

반응형

[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"""처럼 문자열 템플릿 안에 '$'를 넣어야 한다
  • 라이브러리 알선(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
링크
«   2025/01   »
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
글 보관함