In this article, I will try to summarize things from the book “Kotlin in-Depth[Vol-I]: A Comprehensive Guide to Modern Multi-Paradigm Language”. The topic of this chapter is a concept of function. We’ll learn the basic function anatomy and address some important issues such as using named arguments, default values and vararg-style functions.
Variable Arguments(Varags) in java is a method that takes a variable number of arguments. Variable Arguments in Java simplifies the creation of methods that need to take a variable number of arguments.
- The anatomy of a kotlin function (코틀린 함수의 구조): definition and call syntax, specifics of function parameters
- Control structures (제어 구조): conditionals, loops, error handling
- A Kotlin package structure and imports (코틀린 패키지 구조와 임포트)
Functions
- 재사용이 가능하다
- 값을 전달 받고(파라미터) 호출한곳에 결과값을 반환할수있다
함수의 구조
- function에서 온 fun 키워드를 사용해서 함수를 정의한다.
- 함수이름은 fun 키워드 바로 다음에 오고, 변수와 동일하게 아무 이름으로 함수 이름을 정의해줄수있다. (위의 함수이름은 circleArea이다)
- 함수 이름 다음엔 함수에서 쓰일 값들을 전달받을수있는데 (
,
) 콤마로 분리하고 괄호안에 값의 이름과 반환값의 타입을 적어줘야한다. 이를 파라미터라고 부른다. 반환값의 타입은 꼭 적어줘야한다. 그 이유는 컴파일러는 함수 정의에 타입추론을 할 수 없기 때문이다. - 파라미터에 전달 할 값이 없어도 () 괄호는 꼭 있어야한다
- 반환할값이 있을땐 파라미터 옆,
: 반환값타입
으로 정의해준다. 하지만 없을때와 식이 본문인 함수에서는(expression-body)에선 생략이 가능하다. 컴파일러는 반환값타입은 추론할수있지만 반환지점이 여러곳 일 수 있고 이는 타입을 알아내기 어려울 수 있기 때문에 여전히 명시해야한다.
expression-body 함수: fun circleArea(radius: Doule) = PI*radius*radius
반환값을 추론할수 있기 때문..
- 반환할 값은 앞에 return 키워드를 붙여준다.
return 키워드는 함수 실행을 끝내고 호출한쪽에 제어를 돌려준다. 그 다음에 오는 코드는 절대 실행이 되지 않는다. = unreachable code
- 파라미터의 값을 변경 할 수 없다. 이는 코드를 더 이해하고 깔끔하게 만들어내기 때문이다.
- 코틀린은 call by value semantics를 사용한다. 파라미터 값에 호출하는 인자를 복사한다. 하지만 파라미터가 참조타입이라면 이 데이터의 대한 참조가 인자로 복사되기 때문에 값이 바뀔 수 도있다.
아래의 코드에서 값이 변하는걸 확인할 수 있다.
Positional VS Named arguments
위치기반 vs 이름붙은 인자
- 기본적으론 positional arguments 인자 전달 방식을 쓴다. 값을 전달하는 순서대로 파라미터에 전달된다.
- named arguments 쓰게 된다면 순서 상관없이 이름을 명시해 값을 전달하기 때문에 순서가 중요하지않고 헷갈림이 줄어든다.
- 섞어쓰는것도 가능하다.
Overloading and default values
오버로딩한다는 뜻은 같은 이름의 메서드를 여러개 가지면서 매개변수의 유형과 개수도록 정의한다는 뜻이다. 다만 컴파일러가 어떤 함수를 호출해야할지 구분할 수 있도록 파라미터 타입은 모두 달라야한다.
- 파라미터는 같지만 반환값만 다를경우, 컴파일 오류가 발생한다
하지만 default values 를 정해주게 되면 경우에 따라 오버로딩을 쓰지 않아도 된다.
Vararg (Variable Arguments)가변인자
한 메서드의 파라미터의 개수가 정해지지 않았을 때 쓸 수 있다.
- 코틀린은 call by value semantics 를 사용하기때문에 위에서 설명한것과 동일하게 참조값이 넘어 가게 된다면 값이 바뀔 수 도 있다.
- * 를 사용해서 배열을 전달 할 수 있고, 이는 값을 복사해서 넘겨주기 때문에 호출하는 인자의 값은 변하지 않는다.
- 둘이상을 vararg 파라미터로 선언하는것은 안된다. 하지만 , 로 분리한 여러 인자와 * (스프레드)를 섞어서 전달하는건 괜찮다.
- vararg파라미터가 맨 마지막이 아니라면, 이후의 파라미터는 이름 붙은 인자만 전달할 수 있다.
- vararg파라미터를 이름 붙은 인자로 전달 할 수 없지만, 이름 붙은 인자에 (*)를 사용해서 전달하는건 가능하다.
- 디폴트값이 있는 파라미터와 vararg을 섞어 쓰는건 어렵기 때문에 이름붙은 인자와 (*)를 사용해 전달해야한다. 아닐경우 디폴트 파라미터의 인자를 호출해야 사용할 수 있다.
Function scope and visibility 함수의 영역과 가시성
함수의 위치에 따라 세가지로 구분할 수 있다
- 파일에 직접 선언된 최상위 함수
- 어떤 타입 내부에 선언된 멤버함수
- 다른 함수 안에 선언 된 지역 함수
경우에 따라 프로젝트의 나머지 부분으로부터 구현 내용을 숨겨서 보여주고싶거나 코드에서 함수의 영역을 줄이거나 쓰일 위치를 제한할 때 최상의 함수 앞에 private, internal
키워드(가시성 변경자)를 붙일 수 있다.
- private 으로 정의하면 함수가 정의 된 파일 안에서만(스코프) 해당 함수를 쓸 수 있다.
- internal 으로 정의하면 함수가 적용된 모듈 내부에서만 함수를 사용 할 수 있게 제한한다. 코틀린에서의 모듈은 기본적으로 함께 컴파일되는 파일 전부를 뜻한다.
- public 으로 정의하면 어디든 사용이 가능하지만 최상의 함수는 디폴트로 공개 가시성을 갖기 때문에 불필요한 키워드이다.
패키지와 임포트
패키지 디렉티브는 package 키워드로 시작하고 . 으로 구별된 식별자들로 이뤄진 패키지 전체이름이 뒤에 온다. 이 전체 이름은 프로젝트의 전체 패키지 계층에서 루트패키지로부터 지정한 패키지에 도달하기 위한 경로다.
package foo.bar.util
- 같은 패키지 디렉티브를 사용하면 여러파일을 같은 패키지에 넣을 수 있다.
- 패키지를 이루는 최상위 선언에는 타입, 함수, 프로퍼티가 있다.
- 패키지의 이름을 사용해 다른 패키지에 있는 함수를 호출할수 있으나, 너무 길면 가독성이 떨어지기때문에 import 를 사용하면 간단하게 접근 할 수 있다.
- 다른 형태의 임포트로 어떤 영역에 속한 모든 선언을 한꺼번에 하고싶으면 . 뒤에 * 를 붙여주면 된다.
조건문
조건문을 사용하면 조건에 따라 둘이상의 동작중 하나를 선택해 구현하게 할 수 있다.
if 문
if 문을 사용하면 true , false 의 결과값에 따라 대안 중 하나를 선택 할 수 ㅆ다.
- 조건이 참일때 첫 문장을 실행하고, 거짓일때 else { … } 구문을 실행한다.
- else 가 없으면 컴파일 되지 않는다.
범위, 진행, 연산
- 코틀린은 순서가 정해진 값 사이의 수열을 표현하는 몇 가지 타입을 제공한다. for 루프로 어떤 수 범위를 반복해야할 때는 이런 타입이 유용하다.
val chars = ‘a’..’h’ //’a’ 부터 ‘h’까지의 모든 문자
val twoDigits = 10..99 //10부터 99까지의 모든 수
- in / !in 연산을 하면 어떤 값이 범위 안에 들어가있는지 알수 있다.
num in 10..99 // num≥10&& num <=99
num !in 10..99 // !(num in 10.99)
- 이를 이용해서 범위를 만들어 비교도 가능하다
“def” in “abc”..”xyz”
//true
- until (semi closed range)을 이용해 반만 닫힌 범위를 만드는 것도 가능하다
- 연산 끝값이 시작값보다 작으면 빈 범위가 된다.
5 in 5..5
//true
5 in 5 until 5
// false
- downTo 를 이용하면 내려가는 시퀀스를, step 을 이용하면 올라가는 시퀀스를 만들수도 있다. (양수여야한다)
1..10 step 3
//1,4,7,10
15 downTo 9 step 2
// 15, 13, 11, 9
- subString() 를 이용하면 문자열이나 배열의 일부분을 뽑아 낼수 있다.
“Hello, World”.substring(1..4)
// ello
When 문과 여럿중에 하나 선택하기
if 는 정해진 구분에 따라 하나를 선택을 하게 해주는 방면, when 을 이용하면 여러 대안 중 하나를 선택할 수 있다.
- 자바의 switch 문과 비슷하다 하지만 when에서는 임의의 조건을 검사 할 수 있다. 자바와 다르게 코틀린은 어떤 조건을 만족하는 가지만 실행하고 말고 자바는 break 를 만날때까지 실행한다.
루프
주어진 조건이 만족될 때까지 수행하는 세가지 제어 구조를 제공한다. while
, do-whole
, for
이 있다.
- while 은 () 조건이 참이면 계속해서 구문을 실행하고 거짓이 되면 빠져 나온다.
- do-while은 do {} 구문을 먼저 실행하고 그다음에 조건을 검사하므로 최소 한번은 무조건 실행된다라는 점이 있다.
for 루프와 이터러블
for문은 안에 변수를 선언하고 증감하면서 특정 조건에 맞을때까지 구문을 실행한다. 횟수를 알때 사용하기 좋다.
루프 제어 흐름 변경하기 break & continue
만약 루프의 중간에서 종료 조건을 검사하고 싶을때 break 또는 continue 를 쓰면 된다.
- break 는 즉시 루프를 종료시키고 루프 바로 다음문으로 이동하게 만든다
- continue는 현재 루프 이터레이션을 마치고 다음 조건검사로 바로 진행하게 만든다.
- 레이블을 쓰게되면 제어를 옮길 대상 루프를 지정할 수 있다. continue@outerLoop
꼬리 재귀 함수 (tail recursive)
재귀함수: 재귀함수는 호출의 호출을 반복한다. 한마디로 자기 자신을 호출하는 함수인데, 재귀함수를 사용하면 코드가 짧아지고 내용도 직관적으로 파악할 수 있어 가독성이 높아진다는 장점이있다.
- 하지만 엄청난 호출이 일어날 경우 스택에 공간이 다 차버리면 ‘stack over flow’현살을 일으킬수있다. 그래서 사용하는것이 꼬리 재귀함수이다.
- 재귀 호출이 끝나면 아무일도 하지않고 바로 결과만 반환되도록 하는 방법이다.
- 꼬리부분에서 실행되서 return 되기전에 값이 정해지며 호출당한 함수의 결과값 -> 호출하는 함수의 결과값으로 반환된다.
예외처리
코틀린의 예외처리는 다른 언어와 비슷하다. 예외가 발생하면 throw를 사용해 에러를 던지거나, try catch 로 잡아낼수있다. 예외에 대해 처리를 하지 않는 경우 함수 호출 스택을 거슬러 올라가면서 예외를 처리하는 부분이 나올때까지 예외를 다시 던진다. (rethrow)
try, catch 예외처리하기
발생한 예외를 직접 잡아서 처리하고싶을때 쓴다.
예외를 던지면 다음과 같은 일이 벌어진다
- 프로그램은 예외를 잡아내는 핸들러를 찾는다. 예외와 일치하는 핸들러가 있다면 예외 핸들러가 예외를 처리한다.
- 현재 함수 내부에서 핸들러를 찾을 수 없으면 함수 실행이 종료되고 함수가 스택에서 제거된다. 그리고 호출한 쪽의 문맥안에서 예외 핸들러 검색을 수행한다. 그리고 예외를 호출한 호출자에게 전파했다고 말한다.
- 프로그램 진입점에 이를 때까지 예외를 잡아내지 못하면 현재 스레드가 종료된다.
- 자바와 달리 코틀린에서는 비검사예외(unchecked exception)과 검사예외(checked exception)을 구분하지않는다.
체크 예외 : Exception 클래스의 서브클래스이면서 RuntimeException 클래스를 상속하지 않은 것들 (ClaassNotFoundException, CloneNotSupportException, IOException, InstantiationException)
언체크 예외 : RuntimeException을 상속한 클래스 (RuntimeException = ArithmeticException, IllegalArgumentException, IndexOutOfBoundsException)
- try 문의 다른 형태는 finally 블록을 사용한다. finally 블록은 떠나기 전 프로그램이 어떤 일을 수행하도록 만들어준다. 이는 내부에서 할당한 자원을 해제할 때 유용하다.