Kotlin

2장. 코틀린 기초 - 함수와 변수

오초의 기록 2022. 2. 20. 13:33

코틀린 문법과 특성

fun main(args:Array<String>) {
    println("Hello, World!")
}

class Sample02 {

}

/*
[실행결과] 
Hello, World!
*/
  • 함수를 선언할 때 fun 키워드를 사용한다. 
  • 파라미터 이름 뒤에 파라미터의 타입을 쓴다. 변수를 선언할 때도 마찬가지 방식으로 타입을 지정한다. 
  • 함수를 최상위 수준에 정의할 수 있다. (자바와 달리) 꼭 클래스 안에 함수를 넣어야 할 필요가 없다. 
  • 배열도 일반적인 클래스와 마찬가지이다. 코틀린에는 자바와 달리 배열 처리를 위한 문법이 따로 존재하지 않는다. 
  • System.out.println 대신에 println이라고 쓴다. 코틀린 표준 라이브러리는 여러 가지 표준 자바 라이브러리 함수를 간결하게 사용할 수 있게 감싼 래퍼(wrapper)를 제공한다. println도 그런 함수 중 하나다.
  • 줄 끝에 세미콜론(;) 붙이지 않아도 좋다. 

 

함수

  • 함수 선언은 fun 키워드로 시작한다. 
  • fun 다음에는 함수 이름이 온다.  (max) 
  • 함수 이름 뒤에는 괄호 안에 파라미터 목록이 온다. (a: Int, b: Int) 
  • 함수의 반환타입은 파라마터 목록의 닫는 괄호 다음에 오는데, 괄호와 반환 타입 사이를 콜론(:)으로 구분해야 한다. (:Int)
  • 코틀린 if는 (값을 만들어내지 못하는) 문장이 아니고 결과를 만드는 식이다.

코틀린 함수 정의

문(Statement)과 식(expression)의 구분

  • 코틀린에서 if는 식이지 문이 아니다. 
  • 식 : 값을 만들어내며 다른 식의 하위 요소로 계산에 참여할 수 있다.
  • 문 : 자신을 둘러싸고 있는 가장 안쪽 블록의 최상위 요소로 존재하며 아무런 값을 만들어내지 않는다. 
  • 자바에서는 모든 제어 구조가 문인 반면 코틀린에서는 루프를 제외한 대부분의 제어 구조가 식이다. 
  • 대입문은 자바에서는 식이었으나  코틀린에서는 문이 됐다. 

 

식이 본문인 함수

fun max1(a:Int, b:Int):Int = if(a>b) a else b
  • 중괄호를 없애고 return을 제거하면서 등호(=)를 식 앞에 붙이면 더 간결하게 함수를 표현할 수 있다. 
  • 블록이 본문인 함수 : 본문이 중괄호로 둘러싸인 함수 
  • 식이 본문인 함수 : 등호와 식으로 이뤄진 함수 

 

fun max2(a:Int, b:Int) = if(a>b) a else b
  • 식이 본문인 함수의 경우 굳이 사용자가 반환 타입을 적지 않아도 컴파일러가 함수 본문 식을 분석해서 식의 결과 타입을 함수 반환 타입으로 지정해준다. 
    • 타입 추론 : 컴파일러가 타입을 분석해 프로그래머 대신 프로그램 구성 요소의 타입을 정해주는 기능
  • 식이 본문인 함수의 반환 타입만 생략 가능하다는 점 유의
  • 블록이 본문인 함수가 값을 반환한다면 반드시 반환 타입을 지정하고 return 문을 사용해 반환 값을 명시해야 한다. 
    • 이유 : return 문이 여럿 들어있는 경우 반환 타입을 꼭 명시하고 return을 반드시 사용한다면 함수가 어떤 타입의 값을 반환하고 어디서 그런 값을 반환하는지 더 쉽게 알아볼 수 있다. 

 

변수

  • 타입 지정 생략 - 타입으로 변수 선언을 시작하면 타입을 생략할 경우 식과 변수 선언을 구별할 수 없다. 
  • 코틀린에서는 키워드로 변수 선언을 시작하는 대신 변수 이름 뒤에 타입을 명시하거나 생략하게 허용한다. 
  • 식이 본문인 함수에서와 마찬가지로 여러분이 타입을 지정하지 않으면 컴파일러가 초기화 식을 분석해서 초기화 식의 타입을 변수 타입으로 지정한다. 
    val question = "삶, 우주, 그리고 모든 것에 대한 궁긍적인 질문"
    val answer = 42
    val answer2 :Int = 42
    val yearsToCompute= 7.5e6 //부동상수점 상수 - double 타입

 

  • 초기화 식을 사용하지 않고 변수를 선언하려면 반드시 변수 타입을 명시해야 한다.  
  • 초기화 식이 없다면 변수에 저장될 값에 대해 아무 정보가 없기 때문에 컴파일러가 타입을 추론할 수 없다. 따라서 그런 경우 타입을 반드시 지정해야 한다. 
    val answer3 : Int // 초기화 식을 사용하지 않고 변수 선언 - 타입 반드시 명시
    answer3 = 42

 

변경 가능한 변수와 변경 불가능한 변수 

변수 사용시 사용하는 키워드 

val 

  • 변경 불가능한 참조를 저장하는 변수다. val로 선언된 변수는 일단 초기화하고 나면 재대입이 불가능하다.
  • 자바로 말하자면 final 변수에 해당한다. 
  • 값을 뜻하는 value에서 따옴
  • 블록을 실행할 때 정확히 한 번만 초기화되어야 한다. 하지만 어떤 블록이 실행될 때 오직 한 초기화 문장만 실행됨을 컴파일러가 확인할 수 있다면 조건에 따라 val 값을 다른 여러 값으로 초기화할 수 있다. 
val message : String
    if(canPerformOperation()){
        message = "Success" 
        //... 연산을 수행한다. 
    }else{
        message = "fail"
    }
  • val 참조 자체는 불변일지라도 그 참조가 가리키는 객체의 내부 값은 변경될 수 있다는 사실을 기억하라. 
    val languages = arrayListOf("Java") // 불변 참조를 선언한다.
    languages.add("Kotlin") // 참조가 가리키는 객체 내부를 변경한다.

    println(languages)//[Java, Kotlin]

 

var 

  • 변경 가능한 참조다. 이런 변수의 값은 바뀔 수 있다. 
  • 자바의 일반 변수에 해당한다. 
  • 변수를 뜻하는 variable에서 따옴 
  • var 키워드를 사용하면 변수의 값을 변경할 수 있지만 변수의 타입은 고정돼 바뀌지 않는다. 
var answer = 42
answer="no answer" //-- Type mismatch
  • 그 타입(String)이 컴파일러가 기대하는 타입(Int)과 다르기 때문에 오류 발생한다.
  • 어떤 타입의 변수에 다른 타입의 값을 저장하고 싶다면 변환 함수를 써서 값을 변수의 타입으로 변환하거나, 값을 변수에 대입할 수 있는 타입으로 강제 형 변환해야 한다. 

 

기본적으로 모든 변수를 val 키워드를 사용해 불변 변수로 선언하고, 나중에 꼭 필요할 때만 var로 변경해라. 변경 불가능한 참조와 변경 불가능한 객체를 부수 효과가 없는 함수와 조합해 사용하면 코드가 함수형 코드에 가까워진다. 

 

더 쉽게 문자열 형식 지정 : 문자열 템플릿

  • 코틀린에서도 변수를 문자열 안에 사용할 수 있다. 
  • 문자열 리터럴의 필요한 곳에 변수를 넣되 변수 앞에 $를 추가해야 한다. 
  • $문자를 넣고 싶으면 println("\$x")와 같이 \를 사용해 $를 이스케이프(escape) 시켜야 한다. 
    • x의 값이 아닌 $x를 출력한다. 
fun main(args: Array<String>){
    val name = if(args.size > 0) args[0] else "Kotlin"
    println("Hello, $name!")
    //"Bob"을 인자로 넘기면 "Hello, Bob!"을 출력하고 아무 인자도 없으면 "Hello, Kotlin!" 을 출력한다. 
}
  • 문자열 템플릿 안에 사용할 수 있는 대상은 간단한 변수 이름만으로 한정되지 않는다. 복잡한 식도 중괄호({})로 둘러싸여 문자열 템플릿 안에 넣을 수 있다. 
if(args.size>0) {
    println("Hello, ${args[0]} !") // args 배열의 첫 번째 원소를 넣기 위해 ${} 구문을 사용한다.
}
  • 중괄호로 둘러싼 식 안에서 큰 따옴표를 사용할 수 있다. 
println("Hello, ${if (args.size>0) args[0] else "someone"}") //Hello, someone

 

한글을 문자열 템플릿에서 사용할 경우 주의할 점 

unresolved reference 오류 발생하므로 변수 이름을 {}로 감싸야 한다. 문자열 템플릿 안에서 변수 이름만 사용할 경우라도 ${name}처럼 중괄호로 변수명을 감싸는 습관을 들이면 더 좋다. 

val name = if(args.size > 0) args[0] else "Kotlin"
println("Hello, ${name}님 반가워요!")

 

 

question

1. 메소드 안에서는 초기화식 사용하지 않은 변수 선언이 가능하고 클래스 안에서는 가능하지 않은 이유는?