NOW OR NEVER

[Kotlin] 함수 정의와 호출 본문

Android/Kotlin

[Kotlin] 함수 정의와 호출

LAURA 2024. 1. 30. 19:44
반응형

함수 정의와 호출

컬렉션

  • 자바의 getClass() = 코틀린의 javaClass : 해당 객체가 어떤 클래스에 속하는 지 확인 가능
  • 자바 컬렉션 = 코틀린 컬렉션: 코틀린은 자신 만의 컬렉션 기능을 제공하는 것이 아닌 기본 자바 컬렉션을 활용한다
    • 코틀린이 자체 컬렉션을 제공하지 않는 이유
      1. 표준 자바 컬렉션 활용 시 자바 코드와 상호작용하기 훨씬 쉬움
      2. 자바에서 코틀린 함수 호출 혹은 코틀린에서 자바 함수 호출 시 자바와 코틀린 컬렉션을 서로 변환 필요X

함수 호출 간소화

이름 붙인 인자

  • 코틀린에서 사용예시
      joinToString(collection, seperator = "", prefix = " ")
  • 함수에 인자로 전달한 것들이 각각 어떤 역할을 하는 지 구분할 수 있게 도와준다.
  • 디폴트 파라미터 값과 함께 사용 시 더 유용하다.
  • boolean flag값을 전달해야 하는 경우 유용
    • flag: 프로그래밍에서 조건에 따라 true혹은 false값을 넣어주는 boolean형 변수 ex) boolean tempTF = true;
    • 자바
      1. boolean 타입 대신 enum 타입 사용 권장
      2. 파라미터 이름을 주석에 넣어서 사용 요구 ex) joinToString(collection, /*seperator*/ "", /*prefix*/ " ")
  • 코틀린으로 작성한 함수 호출 시 함수에 전달하는 인자 중 일부(또는 전부)의 이름 명시 가능 : 호출 시 인자 중 어느 하나라도 이름을 명시하고 나면 혼돈을 막기 위해 그 뒤에 오는 모든 인자는 이름을 꼭 명시해야 한다.
  • 자바로 작성한 코드 호출 시 이름 붙인 인자 사용 불가능하여 안드로이드 프레임워크나 JDK가 제공하는 함수 호출 시 이름 붙인 인자 사용 불가능

디폴트 파라미터 값

  • 사용 예시
      fun<T> joinToString (
          collection: Collection<T>,
          separator: String = ", ",
          prefix: String = "",
          postfix: String = ""
      ): String
  • 자바에서의 overloading 메서드 많아지는 문제 해결 가능: 코틀린에서는 함수 언언에서 파라미터의 디폴트 값을 지정할 수 있어 오버로드 중 상당수 피할 수 있음
  • 함수 호출 시 모든 인자를 쓸 수 도 있고 일부를 생략할 수 있게 만들어줌
  • 함수의 디폴트 파라미터 값은 함수를 호출하는 쪽이 아닌 함수 선언 쪽에서 지정된다.
  • 자바에서의 디폴트 값
    • 자바에는 디폴트 파라미터 값이라는 개념이 없어 코틀린 함수를 자바에서 호출하는 경우 그 코틀린 함수가 디폴트 파라미터 값을 제공하더라도 모든 인자를 명시해야 함
    • @JvmOverloads
      • 자바 쪽에서 좀 더 편하게 코틀린 함수 호출하고 싶을 때 사용
      • 함수에 추가 시 코틀린 컴파일러가 자동으로 맨 마지막 파라미터로부터 파라미터를 하나씩 생략한 오버로딩한 자바 메서드 추가해줌

최상위 함수와 프로퍼티

  • 코틀린에서는 자바에서 정적인 메서드를 모아두는 역할만 하며 자바에서의 특별한 상태나 인스턴스 메서드는 없는 클래스가 생기는 문제 해결 가능: 객체 지향 언어인 자바에선는 모든 코드를 클래스의 메서드로 작성해야 하기 때문에 이런 문제점이 발생한다.

최상위 함수

  • 함수를 직접 최상위 수준, 모든 다른 클래스 밖에 위치시키기 가능: 여전히 그 파일의 맨 앞에 정의된 패키지의 멤버 함수이므로 다른 패키지에서 그 함수 사용하고 싶을 때는 그 함수가 정의된 패키지를 임포트 해야함, 하지만 임포트 시 유틸리티 클래스 이름이 추가로 들어갈 필요 없음
  • 최상위 함수 실행될 수 있는 이유: JVM이 클래스 안에 들어있는 코드만 실행할 수 있어 컴파일러는 이 파일을 컴파일할 때 새로운 클래스를 정의한다. 코틀린만 사용하는 경우 그냥 그런 클래스가 생긴다는 사실을 기억하면 된다.
  • 코틀린 컴파일러가 생성하는 클래스의 이름은 최상위 함수가 들어있던 코틀린 소스 파일 이름과 대등
  • 코틀린 파일의 모든 최상위 함수는 해당 클래스의 정적인 메서드가 된다.
  • @JvmName
          @file:JvmName("StringFunctions")
          package strings
          fun joinToString(...): String {...}
    • 파일에 대응하는 클래스의 이름을 변경해주는 애너테이션
    • 코틀린 최상위 함수가 포함되는 클래스의 이름을 바꾸고 싶다면 파일에 @JvmName 애너테이션 추가
    • 해당 애너테이션은 파일의 맨 앞, 패키지 이름 선언 이전에 위치

최상위 프로퍼티

  • 함수와 마찬가지로 프로퍼티도 파일의 최상위 수준에 높을 수 있다.
  • 정적 필드에 저장한다.
  • 다른 모든 프로퍼티처럼 접근자 메서드를 통해 자바 코드에 노출(val의 경우 게터, var의 경우 게터, 세터 생성)

확장 함수와 확장 프로퍼티

  • 코틀린의 핵심 목표: 기존 코드와 코틀린 코드 자연스럽게 통합확장 함수 Extension Function
  • 기존 자바 API를 재작성하지 않아도 코틀린이 제공하는 여러 편리한 기능을 사용할 수 있게 하는 역할을 함
  • 어떤 클래스의 멤버 메서드인 것처럼 호출할 수 있지만 그 클래스 밖에 선언된 함수
  • 생성 방법 : 추가하려는 함수 이름 앞에 그 함수가 확장할 클래스 이름을 덧붙이기
          fun String.lastChar() : Char = this.get(this.length - 1)
    • 수신 객체 타입(receiver type): 확장이 정의될 클래스의 타입 ex) String
    • 수신 객체(receiver object): 확장 함수가 호출되는 대상이 되는 값(객체) 즉 확장이 정의될 클래스에 속한 인스턴스 객체 ex) this
  • 자바 클래스로 컴파일한 클래스 파일이 있는 한 그 클래스에 원하는 대로 확장 추가 가능
  • 확장 함수 본문에서도 this 사용 및 생략 가능
  • 확장 함수가 캡슐화를 깨지는 않는다.: 클래스 안에서 정의한 메서드와 달리 확장 함수 안에서는 클래스 내부에서만 사용할 수 있는 비공개(private) 멤버나 보호된(protected) 멤버 사용 불가능
  • 호출 시 확장함수와 멤버 메서드 구분 불가능(그 여부가 중요하지도 않음)

임포트

  • 확장 함수 사용 시 그 함수를 다른 클래스나 함수와 함수와 마찬가지로 임포트해야 한다.
  • as 키워드 사용
    • 임포트 시 as 키워드 사용 시 임포트한 클래스나 함수를 다른 이름으로 부를 수 있다. ex) import strings.lastChar as last
    • 한 파일 안에서 다른 여러 패키지에 속해있는 이름이 같은 함수를 가져와 사용해야 하는 경우 이름 바꿔서 임포트 하면 이름 충돌 방지 가능
    • 확장 함수 이름 충돌을 해결할 수 있는 유일한 방법: 일반적인 클래스나 함수라면 전체 이름(FQN: Fully Qualified Name)을 써도 되지만 코틀린 문법 상 확장 함수는 반드시 짧은 이름을 써야 한다. 따라서 임포트할 때 이름을 바꾸는 것이 확장 함수의 이름 충돌을 해결 할 수 있는 유일한 방법이다.

자바에서의 확장 함수 호출

  • 확장 함수는 수신 객체를 첫 번째 인자로 받는 정적메서드로 자바에서 확장함수 사용은 편하다.
  • 다른 최상위 함수와 마찬가지로 확장 함수가 들어있는 자바 클래스 이름도 확장함수가 들어있는 파일 이름에 따라 결정

유틸리티 함수 정의

  • 확장 함수는 단지 정적 메서드 호출에 대한 문법적인 편의(syntatic sugar)
  • 클래스가 아닌 더 구체적인 타입을 수신 객체 타입 지정 가능

오버라이드 불가능

  • 확장 함수는 오버라이드가 불가능하다.
  • 확장 함수는 클래스의 일부가 아니다. 클래스 밖에 선언된다: 이름과 파라미터가 완전히 같은 확장 함수를 기반 클래스와 하위 클래스에 대해 정의해도 실제로는 확장 함수를 호출할 때 수신 객체로 지정된 변수의 정적 타입에 의해 어떤 확장 함수가 호출될지 결정되지, 그 변수에 저장된 객체의 동적인 타입에 의해 확장 함수가 결정되지 않는다.
  • 오버라이드 할 수 없는 이유: 확장 함수를 첫 번째 인자가 수신 객체인 정적 자바 메서드로 컴파일한다. 이처럼 코틀린은 호출될 확장 함수를 정적으로 결정하기 때문에 확장함수는 오버라이드 할 수 없다.

확장 프로퍼티

  • 확장 프로퍼티 사용 시 기존 클래스 객체에 대한 프로퍼티 형식의 구문으로 사용할 수 있는 API 추가 가능
  • 확장 프로퍼티는 아무 상태도 가질 수 없다: 상태를 저장할 적절한 방법이 없기 때문
    • 초기화 코드에서 계산한 값을 담을 장소가 전혀 없어 초기화 코드도 사용 불가능
  • 일반 프로퍼티와 다른 점은 수신 객체 클래스가 추가된 것 뿐이다.
  • 뒷받침하는 필드가 없어 기본 게터 구현 제공 불가능하여 최소한 게터 꼭 정의 필수
  • 자바에서 확장 프로퍼티 사용 시 항상 게터나 세터 명시적 호출해야 함

컬렉션 처리

  • 코틀린 컬렉션은 자바와 같은 클래스를 사용하지만 더 확장된 API 제공
  • 자바바 라이브러리의 인스턴스인 컬렉션에 대해 코틀린이 새로운 기능을 추가할 수 있었던 것은 확장함수 덕분이다.

가변 인자 함수

  • 자바의 가변 길이 인자(varargs): 메서드를 호출할 때 원하는개수만큼 값을 인자로 넘기면 자바 컴파일러가 배열에 그 값을 넣어주는 기능
  • 코틀린의 가변 길이 인자: 자바와 비슷하다. 다만 문법이 다름. 타입 뒤에 ...붙이는 대신 파라미터 앞에 vararg 변경자 붙임
  • 스프레드 연산자(*)
          fun main(args: Array<String>) {
              val list = listOf("args: ", *args)
              println(list)
          }
    • 스프레드 연산자 통해 배열에 들어있는 값과 다른 여러 값을 함께 써서 함수 호출 가능
    • 자바에서 사용 불가능

중위 호출 infix call

  • ex) 1 to One -> to가 중위 호출
  • 수신 객체와 유일한 메서드 인자 사이에 메서드 이름 넣어야 함(이때 객체, 메서드 이름, 유일한 인자 사이에는 공백 필수)
  • 함수를 중위 호출에 사용하게 허용하고 싶으면 infix 변경자를 함수 선언 앞에 추가해야 함

구조 분해 선언 destructuring declaration

  • ex) val (number, name) = 1 to "one"
  • 하나의 내용으로 두 변수를 즉시 초기화 하는 기능
  • Pair, map, list 등 인스턴스 객체에서 구조 분해 적용 가능
  • 루프에서 활용 가능

문자열과 정규식

문자열 나누기

  • 자바의 split 메서드 괄호 안에 들어가는 것은 정규식(regular expression)이다.
  • 코틀린에서는 자바의 split 대신 여러가지 다른 조합의 파라미터를 받는 split 확장 함수 제공(문자열만 받는 함수, 정규식을 받는 함수 등)

여러 줄 3중 따옴표 문자열

  • ex) """문자열"""
  • 3중 따옴표 사용시 줄 바꿈이 들어있는 프로그램 텍스트를 쉽게 문자열로 생성 가능
  • 여러 줄 문자열(=3중 따옴표 문자열)에는 들여쓰기나 줄 바꿈을 포함한 모든 문자가 들어감
  • 3중 따옴표 문자열 안에 문자열 템플릿 사용 가능
    • 해당 문자열 안에서 이스케이프 불가능: 문자열 템플릿의 시작을 표현하는 $ 문자 삽입 불가능
    • $ 문자 삽입 시 문자열 템플릿 안에 '$'문자 삽입 필수 ex) val price = """${'$'}99.9"""
  • 테스트의 예상 출력 작성 시 가장 완벽한 해법
  • 소스코드에서 더 보기 좋게 하려면 trimMargin 확장함수 사용 추천

코드 정리

  • 좋은 코드의 중요한 특징 중 하나는 중복이 없는 것이다.
  • DRY(Don't Repeat Yourself) 원칙: 반복하지 말라 라는 원칙
  • 자바에서의 DRY 원칙을 지키기 위한 예시
    • 메서드 추출(Extract Method) 리팩토링 활용: 긴 메서드 나눠 각 부분 재활용 -> 각 메서드 관계 파악 힘들어짐
    • 출출 메서드를 내부 클래스 안에 넣으면 코드 깔끔하게 조직은 가능하지만 불필요한 준비 코드 늘어남
  • 로컬 함수
    • 코틀린에서는 함수에서 추출한 함수를 원함수 내부에 중첩시킬 수 있어 자바보다 깔끔히 해결할 수 있다.
    • 자신이 속한 바깥 함수의 모든 파라미터와 변수 사용 가능
    • 확장 함수를 로컬 함수로 정의 가능
    • 중첩된 함수 깊이가 깊어지면 코드 읽기가 어려워지므로 일반적으로 한 단계만 함수를 중첩시키라고 권장

'Android > Kotlin' 카테고리의 다른 글

[Kotlin] Kotlin 기초  (0) 2024.01.22
Comments