본문 바로가기
  • GDG on campus Ewha Tech Blog
3-1기 스터디/Kotlin

[3주차] 함수 정의와 호출

by 하동녹초오레오 2021. 11. 16.

3.1 코틀린에서 컬렉션 만들기

  • 2.3.3절 참고 : setOf 함수를 사용해 집합 만드는 방법 배웠음

숫자로 이루어진 집합 만들어보기

fun main()
{
    val set = hashSetOf(1,7,53)
    
    val list = arrayListOf(1, 7, 53)
    
    val map = hashMapOf(1 to "one", 7 to "seven", 53 to "fifty-three")
   
    println(set.javaClass)
    println(list.javaClass)
    println(map.javaClass)
}
  • map : key - value 쌍의 모임
    • map 생성시 사용된 to : 일반 키워드 x, 함수 o
  • javaClass : 자바의 getClass()에 해당하는 코틀린 코드
  • 위의 코드로 실행하면 ?
    • /* class java.util.HashSet class java.util.ArrayList class java.util.HashMap */
    • 코틀린에는 자체 컬렉션 x
    • 코틀린에는 자체 컬렉션 제공하지 않는 이유 : 자바코드와의 상호작용을 위해
    • 코틀린 컬렉션 클래스 == 자바 컬렉션 클래스
  • 코틀린 컬렉션 클래스 함수
    • last() : 마지막 원소 반환
    • max() : 수로 이루어진 컬렉션에서 최대값 반환
    fun main()
    {
        /*val set = hashSetOf(1,7,53)
        
        val list = arrayListOf(1, 7, 53)
        
        val map = hashMapOf(1 to "one", 7 to "seven", 53 to "fifty-three")
       
        println(set.javaClass)
        println(list.javaClass)
        println(map.javaClass)*/
        
        val strings = listOf("a", "b", "c")
        println(strings.last())
        
        val numbers = setOf(1, 12, 4)
        println(numbers.maxOrNull())
        
        
    }
    
    •  

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

joinToString()

  • 자바컬렉션 디폴트 toString의 출력양식과 다르게 출력하고 싶을 때 사용
    //toString 사용시
    fun main()
    {
        val myList = listOf(1, 4, 13)
        
        println(myList.toString())
      
    }
    
  • joinToString() 함수 초기 구현
    • fun <T> joinToString (...)
      • 제네릭하다.
      • 모든 타입의 컬렉션 처리 가능
      •  
  • fun main()
    {
        val myList = listOf(1, 4, 13)
        
        println(joinToString(myList, "!", "#", "#"))
    		
    		//출력 : #1!4!13#
    	  
    }
    fun <T> joinToString(collection: Collection <T>, separator: String, prefix: String, postfix: String) : String
    {
        val result = StringBuilder(prefix)
        for((index, element) in collection.withIndex())
        {
            if(index > 0) result.append(separator)
            result.append(element)
            
        }
        result.append(postfix)
        return result.toString()
    }

3.2.1 이름 붙인 인자

  • joinToString 초기 구현의 문제점
    • 함수 호출 부분에서 각 문자열이 어떤 역할을 하는지 알기 어려움 ( 가독성이 떨어짐 + 함수호출 코드가 모호함)
      • 해결 : 인자에 이름 붙여서 호출하기
      • println(joinToString(collection = myList, separator = "!", prefix = "#", postfix = "#"))
      • 하나라도 인자에 이름을 명시한다면 그 뒤에 오는 인자도 모두 이름 명시하자 (혼동을 막기 위해)

3.2.2 디폴트 파라미터 값

  • 디폴트 파라미터 사용해서 joinToString 함수선언 수정해보기
    • fun <T> joinToString(collection: Collection <T>, separator: String = ", ", prefix: String = "", postfix: String = "") : String {...}
    • 디폴트 파라미터 값이 있으면 함수 호출시 해당되는 인자를 생략할 수 있다.
    • 이름 붙인 인자와 자주 사용함 (디폴트 값을 사용하고 싶은 인자가 호출시 중간 순서일 경우 유용) : println(joinToString(collection = myList, prefix = "#", postfix = "#"))
  • 자바에서 디폴트 파라미터값을 사용하는 코틀린 함수를 편하게 호출하는 방법 : @JvmOverloads 애노테이션 함수에 추가하기
    • JvmOverloads : 코틀린 컴파일러가 자동으로 맨 마지막 파라미터로부터 파라미터를 하나씩 생략한 오버로딩한 자바메소드를 추가해줌

3.2.3 정적인 유틸리티 클래스 없애기 : 최상위 함수와 프로퍼티

  • 최상위 함수 만들기 : 함수를 소스파일의 최상위 수준에 두기 == 해당 소스파일에서 모든 클래스의 외부
  • 최상위 프로퍼티
    • 프로퍼티도 파일의 최상위 수준에 놓을 수 있음

3.3 메소드를 다른 클래스에 추가: 확장 함수와 확장 프로퍼티

  • 확장함수 : 어떤 클래스의 멤버 메소드인 것 처럼 호출가능 but 그 클래스의 밖에 선언된 함수
  • 확장함수 생성 
    • 확장할 클래스 : String
    • 수신 객체 타입 : Strng
    • 수신 객체 : this
    • 확장이 정의될 클래스 == 해당 클래스에서 확장함수를 자신의 함수처럼 사용한다.
    • 수신 객체 == 해당 확장 함수를 실제로 자신의 함수로 사용할 객체 == 그 클래스 타입의 인스턴스 객체
    • 확장 함수의 사용
    • println("Kotlin".lastChar()) //출력 : n //이때 수신 객체는 "Kotlin"
    • 확장함수 vs 클래스 내부에 정의된 함수
      • 확장함수는 클래스의 함수처럼 사용 가능하지만 클래스 내부에서만 사용 가능한 멤버(접근 지정자 private(해당 함수내에서만), protected(같은 패키지))에는 접근 불가능함
  • fun String.lastChar(): Char = this.get(this.length -1) //this : 수신 객체 //this 없이도 수신 객체에 접근 가능 //fun Stirng.lastChar(): Char = get(length-1) //수신 객체 타입은 확장이 정의될 클래스의 타입이며, 수신 객체는 그 클래스에 속한 //인스턴스 객체이다.
  • package strings fun String.lastChar():Char = this.get(this.length -1)

3.3.1 임포트와 확장함수

  • 확장함수 사용하기 위해서는 임포트를 해야한다.
  • 확장함수 import 하기
    • 확장함수만 import : import strings.lastChar
    • strings 전체를 import : import strings.*
    • as 키워드 사용 : import strings.lastChar as last
    • → println("kotlin".last()) // import 한 소스코드 내에서 last라는 이름으로 사용

3.3.2 자바에서 확장 함수 호출

확장함수가 들어있는 자바 클래스 이름 == 확장 함수가 들어있는 파일 이름

if (확장함수 파일 이름 == StringUtil.kt)

then 확장함수가 들어있는 자바 클래스 이름 = StringUtilKt

3.3.3 확장 함수로 유틸리티 함수 정의

fun <T>      joinToString(collection: Collection <T>, separator: String = ", ", 
prefix: String = "", postfix: String = "") : String
{
    val result = StringBuilder(prefix)
    for((index, element) in collection.withIndex())
    {
        if(index > 0) result.append(separator)
        result.append(element)
        
    }
    result.append(postfix)
    return result.toString()
}
//Collection<T> 에 대한 확장함수를 선언한다. 
fun <T> Collection<T> joinToString(separator: String = ", ", prefix: String = "", 
postfix: String = "") : String
{
    val result = StringBuilder(prefix)
    for((index, element) in this.withIndex())
    {
        if(index > 0) result.append(separator)
        result.append(element)
        
    }
    result.append(postfix)
    return result.toString()
}

3.3.4 확장함수는 오버라이드할 수 없다.

  • View- Button (View의 하위클래스)
    • Button 에서 View의 click 메소드 오버라이딩
    • Button 타입의 변수를 View 타입 변수에 대입하고 click 메소드 호출하면 Button의 클릭 메소드가 호출됨
    • → View 타입 변수로 선언되었더라도 실제 타입인 Button에 따라 호출될 메소드가 결정됨
    • (동적인 타입에 의해 함수가 결정됨)
  • 이름과 파라미터가 완전히 같은 확장함수를 상위클래스와 그 하위클래스에 대해서 정의한다면?
    • 동적으로 타입변환 불가능 ( == 오버라이딩 불가능)
    • 확장 함수 호출시 수신 객체로 지정한 변수의 정적타입에 의해 어떤 함수가 호출될지 결정됨

3.3.5 확장 프로퍼티

  • 확장 프로퍼티
    • 이름은 프로퍼티지만 실제로 상태를 가질 수 없다.
    • 초기하 코드 사용 불가
  • 확장 프로퍼티 선언 : val String.lastChar: Char get() = get(length-1)
    • 확장 프로퍼티는 뒷받침하는 필드 x == 기본 게터 구현 제공 불가
    • ⇒ 게터 정의 필수
  • 변경 가능한 확장 프로퍼티 선언 : 프로퍼티 세터 구현하기
  • var StringBuilder.lastChar: Char get() = get(length -1) set(value: Char) { this.setCharAt(length-1, value) }
  • 확장프로퍼티의 사용 : 멤버 프로퍼티 사용법과 동일
  • println("Kotlin".lastChar)
  • 자바에서 확장 프로퍼티 사용 : 게터, 세터 명시적으로 호출해야함
  • StringUtilKt.getLastChar("Java")

3.4.1 자바 컬렉션 API 확장

  • 어떻게 자바 라이브러리 클래스의 인스턴스인 컬렉션에 대해 코틀린이 새로운 기능을 추가할 수 있을까? → 3장 앞쪽에서 본 last()와 max()는 확장함수였다!!

3.4.2 가변인자 함수 : 인자의 개수가 달라질 수 있는 함수 정의

  • vector<int> v(); vector<int> v2(10); vector<int> v3(10, 1);
  • listOf 등 컬렉션 생성 함수 정의 : fun listOf<T>(vararg values: T) : List<T> { ... }
  • 가변길이 인자 : 메소드를 호출할 때 원하는 개수만큼 값을 인자로 넘기면 컴파일러가 그 값들을 배열에 넣어주는기능

3.4.3 값의 쌍 다루기 : 중위 호출과 구조 분해 선언

val map = mapOf(1 to "one", 7 to "seven", 10 to "ten")
//to 메소드 중위 호출
//객체.메소드명 해당 메소드의 하나뿐인 인자 

//val map = mapOf(1.to("one"), 7.to("seven"), 10.to("ten"))
//top 메소드 일반 호출
  • 중위 호출
    • 인자가 하나뿐인 일반 메소드나 확장함수에 사용 가능
    • 중위 호출 허용 : infix 변경자를 함수 선언 앞에 추가하기
  • to 함수와 중위호출
    • to 함수는 Pair 객체 반환
    • Pair : 두 원소로 이뤄진 순서쌍을 표현하는 클래스
  • infix fun Any.to(other:Any) = Pair(this, other)
  • 구조분해 선언

3.5.1 문자열 나누기

  • 자바에서 "1.2.3".split(".") → .을 구분자로 문자열을 분리할 수 없음
  • → split의 구분 문자열은 실제로는 정규식이기 때문이다. (.)
  • 코틀린에서 : split 확장함수를 오버로딩한 버전으로 해결

3.5.2 정규식과 3중 따옴표 묶은 문자열

  • 코틀린에서는 정규식을 사용하지않고도 문자열을 쉽게 파싱할 수 있다.
  • 정규식의 단점 : 나중에 알아보기 힘들다.
  • 코틀린에서 정규식이 필요할 때 → 코틀린 라이브러리 사용

3.5.3 여러줄 3중 따옴표 문자열

  • 3중 따옴표 문자열
    • 이스케이프를 피할 때
    • 줄바꿈을 포현하는 아무 문자열이나 이스케이프 없이 그대로 들어간다. → 줄바꿈이 있는 프로그램 텍스트를 쉽게 문자열로 만들 수 있다.
    • 내부에 문자열 템플릿 사용 가능

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

  • DRY 원칙 : DONT REPEAT YOURSELF (반복하지 말라)→ 그러나 메소드끼리의 관계 파악이 어려워짐
  • → 반복을 피하기 위해 : 메소드 추출 리팩토링 적용해서 긴메소드를 부분부분 나눠 재활용 가능
  • 반복을 피할 코틀린의 해법 : 추출한 함수를 원함수 내부에 중첩시키기
  • 로컬함수
    • 호출 : 원함수 내부에서 호출해야함
    • 원함수 내부의 함수 == 해당 함수의 로컬함수
    • 자신이 속한 바깥 함수의 모든 파라미터와 변수 사용 가능 (내부함수는 원함수의 파라미터와 변수 사용 가능)
  • 로컬 함수를 확장함수로
    • 원함수에서 확장을 정의할 클래스를 사용하는 경우
    • 로컬함수를 원함수에서 사용하는 클래스의 확장함수로 추출하는 것

댓글