본문 바로가기

Kotlin

[Kotlin] Kotlin 기본 문법 by "Code with Joyce" 2편 - (1)

Code with Joyce님과 함께하는 '코틀린 3강으로 끝내기 - 2편 기본 문법'이다.

 

1. 람다(Lambda)

람다식은 간단히 람다라고도 불리는 익명 함수(이름 없는 함수)이며 우리가 마치 value처럼 다룰 수 있다. 람다는 메서드의 파라미터로 넘겨줄 수 있고 return 값으로도 활용할 수 있다.

 

람다를 선언하는 방법은 다양하다.

 

// 기본형
val lambdaName : Type = { argumentList -> codeBody }

// 람다를 생성하여 greeting에 할당
val greeting: () -> Unit = { println("Hello") }

// number의 자료형이 추론되므로 지정할 필요가 없음
val square1 : (Int) -> (Int) = { number -> number * number }

// 혹은 number에만 자료형을 적어도 무관
val square2 = { number: Int -> number * number }

// return 값도 알아서 추정함
val nameAge = {name: String, age: Int ->
    "My name is ${name} I'm ${age}"
}

//호출
greeting()
square1(12)
square2(10)
nameAge("mimi", 27)

/*
result

    Hello
    144
    100
    My name is mimi I'm 27
*/

 

람다의 인자가 1개인 경우는 인자 대신에 it을 사용하면 된다.

 

// 인자 대신 it 사용
val greeting2: (String) -> String = { "Hello. My name is $it."}
val result = greeting2("chacha")

 

람다식에 when무을 활용하면 다음과 같이 반환 값을 조절할 수 있다.

 

val calculateGrade: (Int) -> String = {
    when (it) {
        in 0..40 -> "fail"
        in 41..70 -> "pass"
        in 71..100 -> "perfect"
        else -> "Invalid input"
    }
}

 

람다를 매개변수로 활용하는 방법도 있다.

 

fun main() {
    
    // number가 특정 값과 동일한지 비교하는 람다식
    val lambda = { number: Double ->
        number == 4.3313
    }
    
    // 실인자로 lambda를 넘기고, invokelambda의 형식인자 lambda에서 number와 double을 비교
    println(invokeLambda(lambda))
    
    // 중괄호 자체가 lambda가 됨
    println(invokeLambda({ it > 3.22 }))
    
    // 아래처럼 표현 가능 & () 생략 가능   
    println("Invoke lambda : " + invokeLambda { it > 3.22 })
}

// input이 Double이고 output이 Boolean인 lambda를 형식인자로 받아서 Boolean 값을 return
fun invokeLambda(lambda: (Double) -> Boolean): Boolean {
    val double = 5.2343
    
    // 람다식에서 4.3313과 5.2343을 비교하고 그 값을 반환하게 됨
    return lambda(double)
}

/*
result
    
    false
    true
    Invoke lambda : : true
*/

 

2. 확장 함수(Extentsion Function)

확장 함수는 Standard Library 또는 다른 사람이 만든 Library와 같이 정의된 클래스에 함수를 추가하는 기능이다.

간단한 예제로 String 클래스에 함수를 추가해보겠다.

 

// 확장 함수
// this는 String.() object를 의미
val pizzaIsGreat: String.() -> String = {
    this + "Pizza is the best!"
}

fun main() {
    
    // String 확장 함수 호출
    println("mimi said ".pizzaIsGreat())
}

/*
result

    mimi said Pizza is the best!
*/

 

확장 함수는 함수 내부에도 정의될 수 있고 매개변수를 가질 수도 있다.

 

// 함수
fun extendString(name: String, age: Int): String {
    // String 확장함수
    // 확장함수의 형식인자가 1개일 경우, it으로 대체 가능
    val introduceMyself: String.(age: Int) -> String = {
        "I am ${this} and ${it} years old"
    }
    return name.introduceMyself(age)
}

fun main() {
    
    // 함수 내부의 확장함수 활용
    println(extendString("mimi", 27))
}

/*
result

    I am mimi and 27 years old
*/

 

확장 함수의 장점은 클래스에 필요한 함수를 추가하여 가독성, 구조적인 간결성 등을 향상한다. Java에서는 해당 기능을 지원하지 않아, 기존 클래스를 상속받아 새로운 클래스를 재정의하거나 클래스 내에 Composition 관계로 객체를 갖고 새로운 기능을 확장하였다.

 

3. Data 클래스

데이터 클래스는 데이터 보관 목적으로 만든 클래스이다. 데이터 클래스의 가장 큰 특징은 속성에 대한 toString(), hashCode(), equals(), copy() 메서드를 자동으로 만들어준다는 것이다.

 

데이터 클래스는 클래스 앞에 data 키워드를 붙여준다.

 

// 비행기 티켓의 정보를 저장하는 data class
data class Ticket(
    val companyName: String,
    val name: String,
    var date: String,
    var seatNumber: Int
)

// data class와 class의 차이를 보기 위해 정의
class TicketNormal(
    val companyName: String,
    val name: String,
    var date: String,
    var seatNumber: Int
)

fun main() {
    val ticketA = Ticket(
        "koreanAir",
        "mimiKim",
        "2020-02-16",
        14)
        
    val ticketB = TicketNormal(
        "koreanAir",
        "mimiKim",
        "2020-02-16",
        14)
        
        // data class의 instance
        println(ticketA)
        
        // class의 instance
        println(ticketB)
}

/*
result

    Ticket(companyName=koreanAir, name=mimiKim, date=2020-02-16, seatNumber=14)
    com.example.hellokotlin.TicketNormal@3796751b
*/

 

그 외의 특징은 다음과 같다.

  • 데이터 클래스의 생성자는 1개 이상의 property를 선언해야 한다.
  • 데이터 클래스의 생성자 property는  val 또는 var으로 선언해야 한다.
  • 데이터 클래스에 abstract, open, sealed, inner를 붙일 수 없다.
  • 데이터 클래스는 상속받을 수 없다.

 

데이터 클래스의 인스턴스의 property를 다른 변수에 대입하려면 다음과 같이 해야 한다.

 

val ticket = Ticket("koreanAir", "mimiKim", "2020-02-16", 14)
val name = ticket.name
val date = ticket.date

 

하지만 데이터 클래스는 데이터 분해 및 대입(Destructuring Declarations)을 지원해줘서 한 줄로 표현할 수 있다.

 

val ticket = Ticket("koreanAir", "mimiKim", "2020-02-16", 14)
val (companyName, name, date, seatNumber) = ticket

// 각 변수에 다음 값들이 대입
// companyName = "koreanAir"
// name = "mimiKim"
// ...

 

위처럼 괄호 안에 변수를 선언해주면 생성자에 정의된 property들이 순서대로 변수에 대입된다.

Python의 언패키징(Unpackeging)을 보는 듯하다.