[Do it! 깡샘의 안드로이드 앱 프로그래밍 with 코틀린] 코틀린의 유용한 기법

코틀린의 유용한 기법을 다루며, 람다 함수와 고차 함수, 함수 타입, 널 안전성에 대해 정리했다. 람다 함수는 익명 함수로 간단하게 정의할 수 있으며, 고차 함수는 함수를 매개변수나 반환값으로 사용하는 함수다. 코틀린은 널 안전성을 지원하여 널 포인트 예외를 방지하는 다양한 연산자를 제공한다.
DriedPollack's avatar
Jun 28, 2024
[Do it! 깡샘의 안드로이드 앱 프로그래밍 with 코틀린] 코틀린의 유용한 기법

🌼람다함수와 고차 함수

💡람다 함수

  • 람다함수는 익명 함수 정의 기법이다.
    • 주로 함수를 간단하게 정의할 때 이용하며 람다식이라고도 한다.
  • 코틀린에서는 고차 함수를 지원한다.
    • 고차 함수는 매개변수나 반환값으로 함수를 이용하는데, 람다 함수는 주고받을 함수를 간단하게 정의할 때 사용한다.
  • 람다 함수는 fun 키워드를 이용하지 않으며 함수 이름이 없고, 중괄호로 표현한다.
    • { 매개변수 -> 함수 본문}
  • 코틀린에서 람다 함수를 사용하는 규칙은 다음과 같다
    • 람다 함수는 { } 로 표현한다.
    • { } 안에 화살표가 있으며 화살표 왼쪽은 매개변수, 오른쪽은 함수 본문이다.
    • 함수의 반환값은 함수 본문의 마지막 표현식이다.
      • // 일반 함수 선언 fun sum(no1: Int, no2: Int): Int { return no1 + no2 } // 람다 함수 선언 val sum = { no1: Int, no2: Int -> no1 + no2 }
  • 람다 함수는 이름이 없으므로 함수명으로 호출할 수 없다. 그래서 보통은 람다 함수를 변수에 대입해 다음과 같이 사용할 수 있다.
    • // 람다 함수 호출문 sum(10, 20)
  • 하지만 람다 함수를 항상 변수에 대입해 사용하지 않아도 된다.
    • // 람다 함수를 선언하자마자 호출 { no1: Int, no2: Int -> no1 + no2 } (10,20)
  • 함수에 매개변수가 항상 있어야 하는 건 아니다. 또한 매개변수가 없을 경우 화살표까지 생략할 수 있다.
    • // 매개변수가 없는 람다 함수 {->println("function call")} // 또는 화살표를 생략한 람다 함수 {println("function call")}
  • 람다 함수의 매개변수가 1개일 때는 매개변수를 선언하지 않아도 함수로 전달된 값을 쉽게 이용할 수 있다.
    • // 매개변수가 1개인 람다 함수 fun main(){ val some = {no: Int -> println(no)} some(10) } // 매개변수가 1개인 람다 함수에 it 키워드 사용 fun main(){ val some : (Int) -> Unit = {println(it)} some(10) }
  • 람다 함수에서는 return 문을 사용할 수 없다. 람다 함수의 반환값은 마지막 줄의 실행 결과다.
    • fun main(){ val some = {no1: Int, no2: Int -> println("in lambda function") no1 * no2 } println("result: $some(10,20)") }

💡함수 타입과 고차 함수

  • 변수는 타입을 가지며 타입을 유추할 수 있을 때를 제외하고는 생략할 수 없다.
    • 따라서 변수에 함수를 대입하려면 변수를 함수 타입으로 선언해야 한다.
  • 함수 타입이란 함수를 선언할 때 나타나는 매개변수와 반환 타입을 의미한다. 함수를 대입할 때는 이러한 함수 타입을 선언하고 그에 맞는 함수 타입을 대입해야 한다.
    • // 일반 함수 선언 fun some(no1: Int, no2, Int): Int{ return no1 + no2 } // 함수 타입을 이용해 함수를 변수에 대입 val some: (Int, Int) -> Int = { no1: Int, no2: Int -> no1 + no2 }
  • 함수 타입을 typealias를 이용해 선언할 수 있다. typealias는 타입의 별칭을 선언하는 키워드로, 함수 타입뿐만 아니라 데이터 타입을 선언할 때도 사용한다.
    • // 타입 별칭과 사용 typealias MyInt = Int fun main(){ val data1: Int = 10 val data2: MyInt = 10 }
    • typealias는 주로 변수보다 함수 타입을 선언하는데 사용한다.
      • // 함수 타입 별칭 typealias MyFunType = (Int, Int) -> Boolean fun main() { val someFun: MyFunType = { no1: Int, no2: Int -> no1 > no2 } println(someFun(10, 20)) println(someFun(20, 10)) }
  • 람다 함수를 정의할 때 매개변수의 타입을 유추할 수 있다면 타입 선언을 생략할 수 있다.
    • // 매개변수 타입을 생략한 함수 선언 typealias MyFunType = (Int, Int) -> Boolean val someFun: MyFunType = { no1, no2 -> no1 > no2 }
    • typealias를 이용할 때 뿐만이 아니라 타입을 유추할 수 있는 상황이라면 어디서든 통한다.
      • // 매개변수 타입 선언 생략 예 val someFun: (Int, Int) -> Boolean = { no1, no2 -> no1 > no2 }
    • 또한 함수의 타입을 유추할 수 있다면 변수를 선언할 때 타입을 생략할 수도 있다.
      • // 변수 선언 시 타입 생략 val someFun = { no1: Int, no2: Int -> no1 > no2 }
  • 고차 함수한 함수를 매개변수로 전달받거나 반환하는 함수를 의미한다.
    • // 고차 함수 fun hofFun(arg: (Int) -> Boolean): () -> String { val result = if (arg(10)) { "valid" } else { "invalid" } return { "hofFun result : ${result}" } } fun main() { val result = hofFun({ no -> no > 0 }) println(result()) }
 

🌼널 안전성

💡널 안전성이란?

  • 널이란 객체가 선언되었지만 초기화되지 않은 상태를 의미한다.
  • 널인 상태의 객체를 이용하면 널 포인트 예외가 발생한다.
  • 이때 널 안전성이란 널 포인트 예외가 발생하지 않도록 코드를 작성하는 것을 말한다.
    • 널 안전성을 갖추는 노력을 개발자가 한다면 객체가 null인지 일일이 점검하는 코드를 작성해야 한다.
    • 하지만 프로그래밍 언어가 널 안전성을 지원한다는 것은 객체가 널인 상황에서 널 포인터 예외가 발생하지 않도록 연산자를 비롯해 여러 기법을 제공한다는 의미이다.
      • // 널 안전성을 개발자가 고려한 코드 fun main() { var data: String? = null val length = if (data == null) { 0 } else { data.length } println("data length: ${length}") } // 코틀린이 제공하는 널 안전성 연산자를 이용한 코드 fun main() { var data: String? = null // 데이터가 널이면 0을 반환, 아니면 length를 이용해 문자열의 개수 반환 println("data length: ${data?.length ?: 0}") }
  • 코틀린에서는 변수 타입을 널 허용과 널 불허로 구분한다.
    • 타입 뒤에 ? 연산자를 추가하면 널 허용 변수가 되어 null을 대입할 수 있다.
      • // 널 허용과 너 불허 var data1: String = "kkang" data1 = null // 오류 var data2: String? = "kkang" data2 = null // 성공
  • 널 허용으로 선언한 변수는 얼마든지 널 포인트 예외가 발생할 수 있으므로, 변수가 널인 상황을 고려해 프로그램을 작성해야 한다.
    • ?. 연산자는 변수가 null이 아니면 멤버에 접근하지만 null이면 멤버에 접근하지 않고 null을 반환한다.
      • // 널 포인트 예외 오류 var data: String? = "kkang" var length = data.length // 오류 // 널 안전성 호출 연산자 사용 var data: String? = "kkang" var length = data?.length // 성공
  • 엘비스 연산자란 ?: 기호를 말한다. ?. 연산자는 변수가 null이면 null을 반환한다. 하지만 어떤 경우에는 변수가 null일때 대입해야 하는 값이나 실행해야 하는 구문이 있을 수도 있다. 이 때 엘비스 연산자를 이용한다.
    • ?. 다음에 오는 것은 변수가 null이 아닐때에만 실행이 되고 만약 변수가 null일 경우 ?: 뒤에 오는 코드를 실행한다.
      • // 엘비스 연산자 사용 fun main(){ var data: String? = "kkang" println("data = $data : ${data?.length ?: -1}") // kkang, 5 data = null println("data = $data : ${data?.length ?: -1}") // null, -1 }
  • !!는 객체가 널일 때 예외를 일으키는 연산자다. 어떤 경우에는 널 포인트 예외를 발생시켜야 할 때도 있다. 이때 !! 연산자를 사용한다.
    • // 예외 발생 연산자 fun some(data: String?): Int{ return data!!.length } fun main(){ println(some("kkang")) // 5 println(some(null)) // NullPointerException }
 

🏁결론

코틀린의 유용한 기법을 다루며, 람다 함수와 고차 함수, 함수 타입, 널 안전성에 대해 정리했다. 람다 함수는 익명 함수로 간단하게 정의할 수 있으며, 고차 함수는 함수를 매개변수나 반환값으로 사용하는 함수다. 코틀린은 널 안전성을 지원하여 널 포인트 예외를 방지하는 다양한 연산자를 제공한다.
Share article

More articles

See more posts

👨🏻‍💻DriedPollack's Blog