Languages

Kotlin의 함수 (Collection, 확장함수, 가변 인자 함수, 중위호출)

!쪼렙조햄 2020. 4. 16. 21:32
반응형

3장. 함수 정의와 호출

🎃 Kotlin 에서 Collection 만들기

val set = setOf(1,2,3)
val set2 = hashSetOf(1,7,53)
val list = arrayListOf(1,7,53)
val map = hashMapOf(1 to “one”, 7 to “answer”, 53 to “fifty-three”)
  • to : 언어가 제공하는 특별한 키워드가 아니라 일반 함수이다
    • 중위함수 : 인자가 1개인 함수
  • .javaClass
    • 객체가 어떤 클래스에 속하는지를 볼 수 있다
    • java에서는 getClass()
    • set2.javaClass
      • class java.util.HashSet
    • kotlin이 자신만의 컬렉션 기능을 제공하지 않는다는 의미
  • kotlin이 자체 컬렉션을 제공하지 않는 이유
    • 표준 자바 컬렉션을 활용하여 자바와 상호작용
    • 자바에서 코틀린 함수를, 코틀린에서 자바 함수를 호출할때 컬렉션 변화 불필요
    • 그러나 자바보다 더 많은 기능 사용 가능
      • .last()
      • .max()
  • set vs hashSet
    • set
      • immutable
    • hashset
      • mutable
    • mutable 타입 set
      • mutableSet, hashSet, linkedSet, sortedSet등

👻 함수를 호출하기 쉽게 만들기

val list = listOf(1,2,3
println(list)
  • 위와 같이 코드를 수행했을때 결과 값은 [1, 2, 3]
  • java Collection 에는 디폴트 toString 구현이 들어있다
  • kotlin에서 함수 호출시 인자 중 어느 하나에라도 이름을 명시하고 나면 혼동을 막기 위해 그 뒤에 오는 모든 인자의 이름을 명시할 것
  • 디폴트 구현과 다르게 출력하고 싶어서 joinToString 함수를 만들었다고 가정
  • 함수에 전달하는 인자의 이름 명시
    • 자바로 작성한 코드를 호출할 때는 이름붙인 인자를 사용할 수 없다

🤡 정적인 유틸리티 클래스 없애기

  • 자바에서는 모든 코드를 클래스의 메소드로 작성해야 함
  • 어느 한 클래스에 포함시키기 어려운 코드인 경우
    • 이러한 정적 메소드를 모아두는 클래스가 생긴다
      • ex. JDK의 Collections
    • kotlin에서는 함수를 직접 소스파일의 최상위 수준, 모든 다른 클래스의 밖에 위치시키면 됨
      • 파일의 맨앞에 정의된 패키지의 멤버 함수이므로 다른 패키지에서 사용하고 싶을때는 해당 패키지를 임포트 해서 사용
      • joinToString 함수를 strings 패키지에 넣어서 사용 가능
  • 자바에는 디폴트 파라미터 값이라는 개념이 없다
    • 코틀린 함수를 자바에서 호출하는 경우에는, 코틀린 함수가 디폴트 파라미터 값을 제공하더라도 모든 인자를 명시해야 함
    • @JvmOverloads
      • 코틀린 컴파일러가 파라미터 경우의 수 모두 가진 자바 메소드 만들어줌

💀 메소드를 다른 클래스에 추가

  • 확장함수
    • 어떤 클래스의 멤버 메소드인 것처럼 호출할 수 있지만, 그 클래스 밖에 선언된 함수
    • 만드는법
      • 추가하려는 함수 이름 앞에 그 함수가 확장할 클래스의 이름을 붙여줌
        package strings
        fun String.lastChar(): Char = this.get(this.length - 1)
  • String
    • 수신객체 타입
    • 함수가 확장할 클래스의 이름
  • this
    • 수신객체
    • 확장함수가 호출되는 대상이 되는 값
  • String의 소스코드를 알지도 못하지만 원하는 메소드를 추가할 수 있다

👽 확장 프로퍼티

  • 기존 클래스 객체에 대한 프로퍼티 형식의 구문으로 사용할 수 있는 API 추가 가능
    val StringBuilder.lastChar: Char
      get() = get(length - 1)   
      set(value : Char){
          this.setCharAt(length -1, value)
      }
  • 확장 함수의 경우 최소한 getter는 꼭 정의를 해야한다
    • 뒷받침하는 필드가 없기 때문
    • 뒷받침하는 필드 : 프로퍼티의 값을 저장하기 위한 필드
  • 확장 함수와 같이 확장 프로퍼티도 일반 프로퍼티와 같은데 단지 수신 객체 클래스만 추가됨

👾 가변 인자 함수

  • 자바의 가변길이 인자
    • 메소드를 호출할 때 원하는 개수 만큼 값을 인자로 넘기면, 자바 컴파일러가 배열에 그 값을 넣어주는 기능
    • … 으로 표현
  • 코틀린의 가변 길이 인자
    • vararg 변경자로 표시
  • 이미 배열에 들어있는 원소를 가변 길이 인자로 넘기는 경우
    • 자바 : 배열을 그냥 넘기면 된다
    • 코틀린
      • 배열을 명시적으로 푼 후 배열의 각 원소가 인자로 전달되도록 한다
      • 스프레드 연산자 사용
      • 코드 상에서는 배열 앞에 * 를 붙여주기만 한다

😈 중위 호출

val map = mapOf(1 to “one”, 7 to “seven”)
  • 코틀린 함수 호출의 가독성을 향상시키는 방법 중 하나
    • 코틀린에서 맵 생성시 mapOf 함수 사용
  • to
    • 코틀린 키워드가 아닌 중위 호출 방식에서 to라는 메소드를 호출한 것
      ```

1.to(“one”)
1 to “one"

```

  • 위 : to 메소드를 일반적인 방식으로 호출
  • 아래 : to 메소드를 중위호출 방식으로 호출
  • 인자가 하나뿐인 일반 메소드/확장함수에 중위호출을 사용할 수 있다
  • 함수를 중위호출로 사용하고 싶으면 infix 변경자를 함수 선언 앞에 추가해야 한다
  • 구조 분해 선언

👹 문자열과 정규식

  • 코틀린의 문자열은 자바 문자열과 같다
  • java의 split : 문자열 나누기
    • java의 split 메소드에 불만을 가지는 사람들도 많다
    • .split(“.”)을 했을 때 예상대로 되지 않는다
      • 빈 배열을 반환한다
    • split의 구분 문자열은 실제로는 정규식
      • .은 모든 문자를 나타내는 정규식이 되어버린다
  • kotlin의 split
    • 여러가지의 다른 조합의 파라미터를 받는 확장함수
    • 정규식을 파라미터로 받는 함수는 Regex 타입의 값을 받고, String 타입 받는 경우에는 생각한 대로 동작
    • String.toRegex()
      • String 값을 정규식으로 만들어준다
    • .split(“.”, “-“)
      • 구분 문자열을 하나 이상의 인자로 받을 수도 있다
  • string 확장함수
    • substringBeforeLast, substringAfterLast등의 확장함수도 있다
    • trimMargin
      • 선행 공백을 제거해준다
  • 3중 따옴표 문자열
    • 역슬래시를 포함한 모든 문자를 이스케이프 할 필요가 없다
      • 역슬래시 표현을 \ 말고 \ 한번만 해도 된다
    • 줄바꿈이 들어있는 텍스트를 문자열로 만들 수 있다
    • 3중 따옴표 문자열 안에 문자열 템플릿을 사용할 수도 있다.
      • 3중 따옴표 문자열 안에서는 이스케이프를 할 수 없어서 $를 표현하기 위해서는 $를 문자열 템플릿 안에 넣어 사용해야 한다
      • ${‘$’}
  • regex.matchEngire
    • match 하면
      • matchResult.destructured
    • match 하지 않으면
      • null

😾 코드 다듬기 : 로컬 함수와 확장

  • DRY : Don’t Repeat Yourself
    • 좋은 코드의 중요한 특징중 하나가 중복이 없는 것
    • 자바 코드를 작성할 때는 많은 경우 메소드 추출 리팩토링을 적용해서, 긴 메소드를 부분부분 나눠서 각 부분을 재활용
    • 클래스 안에 작은 메소드가 많아지고, 각 메소드의 관계를 파악하기 힘들어서 오히려 코드를 이해하기 어려워 질 수도 있다
    • 코틀린에서의 해법
      • 함수에서 추출한 함수를 원 함수 내부에 중첩시킨다
      • 로컬 함수는 자신이 속한 바깥 함수의 모든 파라미터와 변수를 사용할 수 있다

👍 스터디 이후 해소된 의문점

  • String.lastChar 선언 코드를 위치는 상관 없이 import 패키지명 만으로 불러 쓸 수 있는가?
    • 확장함수는 코드의 최상위단에 선언을 해주어야 한다
    • 그런 후 package import만으로 불러와서 사용할 수 있다
  • 코틀린은 호출될 확장 함수를 정적으로 결정하기 때문에 확장함수를 오버라이드 할 수 없다..?
    • 정적으로 결정..?
    • 변수나 메소드를 선언할 당시의 타입 사용
    • 현재 타입과는 관련없이 선언 당시의 타입을 따른다는 뜻
  • 검증로직은 User을 사용하는 다른곳에서는 쓰이지 않는 기능이기 때문에 User에 포함시키고 싶지는 않다 (해석)
    • import package.User 만 하면 확장함수까지는 import 되지 않는다
    • 확장함수도 다른곳에서 쓰려고 하면 import package.User.validateBeforeSave 이렇게 import 해야 한다
    • 이런식으로 확장함수로 하면 한번 숨겨진다

Reference

반응형