문제시 연락주시면 삭제하겠습니다.
본문이 중괄호로 둘러싸인 함수를 블록이 본문이 함수라 부르고, 등호와 식으로 이뤄진 함수를 식이 본문이 함수라고 부른다.
fun circleArea2(radius: Double) = PI*radius*radius // 반환값이 Double로 추론됨
컴파일러가 함수 본문 식을 분석해 식의 결과 타입을 함수 반환 타입으로 정해준다. (타입 추론)
fun mul(a: Int, b: Int) = a*b // 1
fun mul(a: Int, b: Int, c: Int) = a*b*c // 2
fun mul(s: String, n: Int) = s.repeat(n) // 3
fun mul(o: Any, n: Int) = Array(n) { o } // 4
mul(1, 2) // Int가 Any의 하위타입이므로 1과 4중에 1을 선택
// mul(1, 2L) // error: none of the following functions can be called with the arguments supplied
// 오류: (Int, Long) 타입을 받을 수 있는 함수가 없음
mul(1L, 2) // Long, Int 타입을 받을 수 있는 함수는 4번뿐이므로 4를 선택
mul("0", 3) // String이 Any의 하위타입이기 때문에 3과 4중에 3을 선택
---------------------------------------------------------------------------------
fun mul2(a: Int, b: Int = 1) = a*b // 1
fun mul2(a: Int, b: Long = 1L) = a*b // 2
fun mul2(a: Int, b: Int, c: Int = 1) = a*b*c // 3
//mul2(10) // error: overload resolution ambiguity
// 오류: 1과 2 사이에 어느 쪽을 호출할지 결정할 수 없음
mul2(10, 20) // 인자가 더 적기 때문에 1과 3 중에 1을 선택
mul2(10, 20, 30) // 적용 가능한 함수가 3번뿐이어서 3을 선택
장점으로는 사용할 경우 어떤 인자에 어떤 인자가 쓰이는지 가시성이 좋아지고, 모든 인자를 이름 붙은 인자로 사용할 경우 함수의 인자의 순서에 영향을 받지 않는다.
단점으로는 가독성과 안전성의 향상에는 비용이 따르기 때문에 이름 붙은 인자도 예외가 아니다. 또한 인자가 많아 지게되면 코드가 길어지고 장황스러워 지고, 인수 이름을 변경하면 함수를 호출하는 이름도 바뀌어야 합니다.
fun printSorted(vararg items: Int) {
items.sort()
println(items.contentToString())
}
printSorted(6, 1, *intArrayOf(3, 8), 2) // 6,1,3,8,2 순서로 원소가 들어있는 배열이 전달되고, [1, 2, 3, 6, 8]이 반환됨
printSorted(items = intArrayOf(1, 2, 3)) // vararg의 이름을 지정할 때는 배열을 그냥 넘기면 됨
코틀린의 varargs는 자바의 vargs와 동일하지만 구문은 약간 다르다. 세 개의 점이 있는 대신 파라미터에 코틀린에서는 vararg를 사용 한다.
Kotlin과 Java의 또 다른 차이점은 Java에서는 배열을 그대로 전달하지만 Kotlin에서는 배열을 명시적으로 풀어야 한다.
Unit의 가장 큰 이유는 제네릭이다.
모든 함수는 값을 반환해야 한다. 코틀린은 자바에서처럼 특별한 형태의 보이드를 갖는 대신 클래스로 표현하기로 결정했다. 클래스를 사용하는 이유는 형식 시스템을 형식 계층의 일부로 만들어 보다 일관되게 만들 수 있기 때문이다.
예를 들어, Worker<T>
라는 일반 인터페이스가 있다고 하자. 이 인터페이스의 doWork() 함수는 값 T를 반환해야 한다.
interface Worker<T> {
fun doWork(): T
}
그러나 Worker 인터페이스를 확장하는 LogWorker 클래스에서 값을 반환할 필요가 없는 작업(예: 로그 작업)에 이 인터페이스를 사용할 수도 있다.
class LogWorker : Worker<Unit> {
override fun doWork() {
// Do the logging
}
}
이것은 원래 값을 반환하도록 설계된 기존 인터페이스를 사용할 수 있는 Unit의 마법이다. doWork() 함수가 반환할 것이 없는 목적을 위해 단위 값을 반환하도록 한다. 따라서 일반 매개 변수를 반환하는 함수를 재정의할 때 유용하다.
또한 doWork() 함수에 대한 Unit return type 언급은 생략했고 Return 구문을 작성할 필요도 없다.
fun error(message: Any): Nothing = throw IllegalStateException(message.toString())
또한 Any?가 모든 타입의 슈퍼타입이듯이 Nothing은 모든 타입의 서브타입이다. 따라서 다음과 같이 작성이 될 수 있다.
val user: User = request.user ?: error("User not found")
fun getUser(request: Request): User {
return request.user ?: error("User not found")
}
fun deleteFiles(files: List<File>? = null) {
if (files != null) files.forEach { it.delete() }
}
이 함수의 설계상의 문제는 List<File>
이 비어 있는지 null인지 또는 요소가 있는지 여부를 전달하지 않는다는 것 이다. 또한 사용하기 전에 리스트가 null인지 확인해야 한다.
이 문제를 해결하기 위해 Null Object 패턴을 사용한다. Null Object 패턴 패턴에서는 객체의 부재를 전달하기 위해 null 참조를 사용하는 대신 인터페이스를 구현하지만 메서드 본문은 비워두는 객체를 사용한다.
따라서 Nothing을 쓴다면 다음과 같이 깔끔하게 작성이 가능하다.
// This function is already defined in the Kotlin standard library
fun emptyList() = object : List<Nothing> {
override fun iterator(): Iterator<Nothing> = EmptyIterator
...
}
fun deleteFiles(files: List<File> = emptyList()) {
files.forEach { it.delete() }
}
interface Worker<T> {
T doWork();
}
class LogWorker implements Worker<Void> {
@Override public Void doWork() {
// Do the logging
return null;
}
}
Void를 사용할 때, 우리는 Void를 반환 유형으로 사용해야 하고(넘길 수 없음) 반환문을 작성해야 하는 반면, Unit의 경우에는 둘 다 건너뛸 수 있다. 이것은 코틀린 코드에서 보이드를 사용하지 않는 또 다른 이유이다.
공개 최상위 함수는 프로젝트 어디서에도 호출이 가능하지만, 비공개 최상위 함수는 파일 내에서만 호출이 가능하다.
자바의 경우 조건에 해당되는 모든 케이스를 실행을 시키므로 break를 걸어야 하지만(이런 경우를 폴스루라고 한다.) 코틀린은 조건에 해당되는 경우만 실행시키고 빠져 나간다.
코틀린은 catch (FooException || BarException e)
와 같은 구문을 사용할 수 없다. 또한 자바는 try-catch문에서 도달 할 수 없는 죽은 코드를 허용하지 않지만 코틀린은 허용하며, 코틀린에서는 검사 예외와 비검사 예외를 구분하지 않는다.
그리고 코틀린에서는 try도 식으로 쓸 수 있다.
fun readInt(default: Int) = try {...}