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. 메소드 안에서는 초기화식 사용하지 않은 변수 선언이 가능하고 클래스 안에서는 가능하지 않은 이유는?