보통 return문은 함수 등에서 값을 반환하는데 사용한다. 하지만 코드의 흐름을 중단하고 함수 등을 빠져 나가기 위해서도 return문을 사용할 수 있다.
fun add(a: Int, b: Int): Int{
return a + b
println("이 코드는 실행되지 않습니다.") // 여기에 도달 X
}
// 1. Unit을 명시적으로 반환
fun hello(name: String): Unit {
println(name)
return Unit
}
// 2. Unit 이름을 생략한 반환
fun hello(name: String): Unit {
println(name)
return
}
// 3. return문 자체를 생략
fun hello(name: String) {
println(name)
}
package chap05.section3
inline fun inlineLambda(a: Int, b: Int, out: (Int, Int) -> Unit){
out(a, b)
}
fun retFunc(){
println("start of retFunc")
inlineLambda(13, 3) { a, b ->
val result = a + b
if(result > 10) return // 10보다 크면 이 함수를 빠져 나감. (비지역 반환)
println("result: $result") // 10보다 크면 이 문장에 도달하지 못함.
}
println("end of retFunc")
} // 여기로 빠져 나감.
fun main() {
retFunc()
}
start of retFunc
inlineLambda 함수의 두 인자의 합이 10 이하가 되도록 하면, 아래와 같은 결과가 나온다.
start of retFunc
result: 6
end of retFunc
이처럼 람다식 내부에서 return을 사용하면, 람다식 외부의 함수까지 의도치 않게 종료되는 '비지역 반환'이 일어날 수 있기 때문에, 라벨 표기를 함께 사용해줘야 한다.
인라인(inline)으로 선언되지 않은 람다식에서 return을 사용할 때는 그냥 사용할 수 없으며, return@label과 같이 라벨(label) 표기와 함께 사용해야 한다. 라벨이란 코드에서 특정한 위치를 임의로 표시한 것으로, @ 기호 뒤에 이름을 붙여서 사용합니다.
package chap05.section3
fun inlineLambda(a: Int, b: Int, out: (Int, Int) -> Unit){ // inline 제거
out(a, b)
}
fun retFunc(){
println("start of retFunc")
inlineLambda(13, 3) lit@{ a, b -> // 1. 람다식 블록의 시작 부분에 라벨 지정
val result = a + b
if(result > 10) return@lit // 2. 라벨을 사용한 블록의 끝부분으로 반환
println("result: $result")
} // 3. 이 부분으로 빠져 나감.
println("end of retFunc") // 4. 이 부분이 실행됨.
}
fun main() {
retFunc()
}
start of retFunc
end of retFunc
람다식 표현 블록에 라벨의 이름을 직접 지정하지 않고, 람다식 함수의 이름을 그대로 라벨처럼 사용할 수 있는데, 이를 암묵적 라벨이라고 한다.
package chap05.section3
fun inlineLambda(a: Int, b: Int, out: (Int, Int) -> Unit) { // inline 제거
out(a, b)
}
fun retFunc(){
println("start of retFunc")
inlineLambda(13, 3) { a, b ->
val result = a + b
if(result > 10) return@inlineLambda
println("result: $result")
} // 여기로 빠져 나감.
println("end of retFunc")
}
fun main() {
retFunc()
}
start of retFunc
end of retFunc
익명함수는 내부에서 라벨을 사용하지 않고 단순히 return만 사용하더라도 비지역 반환이 일어나지 않는다. 따라서 일반 함수의 반환처럼 편하게 사용할 수 있다.
package chap05.section3
fun inlineLambda(a: Int, b: Int, out: (Int, Int) -> Unit) { // inline 제거
out(a, b)
}
fun retFunc(){
println("start of retFunc")
inlineLambda(13, 3, fun(a, b) { // 일반 익명 함수는 비지역 반환이 일어나지 않음.
val result = a + b
if(result > 10) return // 라벨 없이도 사용 가능
println("result: $result")
}) // inlineLambda 함수의 끝
println("end of retFunc")
}
fun main() {
retFunc()
}
start of retFunc
end of retFunc
package chap05.section3
val getMessage = lambda@ { num: Int ->
if(num !in 1..100){
return@lambda "Error" // 레이블을 통한 반환
}
"Success" // 람다식은 return을 안 써줘도 마지막 식이 반환됨.
} // 여기로 빠져 나감.
val getMessage2 = fun(num: Int): String {
if(num !in 1..100){
return "Error"
}
return "Success" // 일반 익명 함수는 return을 꼭 써줘야 함.
}
fun main() {
println(getMessage(5))
println(getMessage(10000))
println(getMessage2(5))
println(getMessage2(10000))
}
Success
Error
Success
Error
package chap05.section3
fun main() {
for(i in 1..5){
if(i == 3) break
print(i) // 12
}
println()
println("outside")
}
12
outside
package chap05.section3
fun main() {
for(i in 1..5){
if(i == 3) continue
print(i) // 1245
}
println()
println("outside")
}
1245
outside
fun labelBreak(){
println("labelBreak")
println("(i, j)")
for(i in 1..5){
for(j in 1..5){
if(j == 3) break
println("($i, $j)")
}
println("after for j")
}
println("after for i")
}
labelBreak
(i, j)
(1, 1)
(1, 2)
after for j
(2, 1)
(2, 2)
after for j
(3, 1)
(3, 2)
after for j
(4, 1)
(4, 2)
after for j
(5, 1)
(5, 2)
after for j
after for i
fun labelBreak2(){
println("labelBreak2")
println("(i, j)")
first@ for(i in 1..5){
second@ for(j in 1..5){
if(j == 3) break@first // 빠져나갈 위치를 라벨로 지정
println("($i, $j)")
}
println("after for j")
} // 여기로 빠져 나감.
println("after for i")
}
labelBreak2
(i, j)
(1, 1)
(1, 2)
after for i
fun labelContinue(){
println("labelContinue")
println("(i, j)")
for(i in 1..5){
for(j in 1..5){
if(j == 3) continue
println("($i, $j)")
}
println("after for j")
}
println("after for i")
}
labelContinue
(i, j)
(1, 1)
(1, 2)
(1, 4)
(1, 5)
after for j
(2, 1)
(2, 2)
(2, 4)
(2, 5)
after for j
(3, 1)
(3, 2)
(3, 4)
(3, 5)
after for j
(4, 1)
(4, 2)
(4, 4)
(4, 5)
after for j
(5, 1)
(5, 2)
(5, 4)
(5, 5)
after for j
after for i
fun labelContinue2(){
println("labelContinue2")
println("(i, j)")
first@ for(i in 1..5) {
second@ for (j in 1..5) {
if (j == 3) continue@first // j가 3이면
println("($i, $j)")
}
println("after for j")
} // 여기로 이동함.
println("after for i")
}
labelContinue2
(i, j)
(1, 1)
(1, 2)
(2, 1)
(2, 2)
(3, 1)
(3, 2)
(4, 1)
(4, 2)
(5, 1)
(5, 2)
after for i
프로그램 코드를 작성하다 보면 해당 코드가 제대로 작동하지 못하고 중단되는 현상이 발생할 수 있는데, 이를 예외(exception)라고 한다. 대부분의 에러(error)는 코드를 작성하는 도중에 컴파일러가 잡아낼 수 있다. 하지만 메모리 부족이나 파일이 손상되는 등의 실행 도중의 잠재적인 오류까지 검사할 수 없기 때문에 정상적으로 실행이 되다가 비정상적으로 프로그램이 종료되는 경우를 예외라고 한다.
package chap05.section4
fun main() {
val a = 6
val b = 0
val c: Int
try{
c = a / b // 0으로 나눔.
}catch (e: Exception){
println("Exception is handled.")
}finally {
println("finally 블록은 항상 반드시 실행됨.")
}
}
Exception is handled.
finally 블록은 항상 반드시 실행됨.
package chap05.section4
fun main() {
val a = 6
val b = 0
val c: Int
try{
c = a / b // Division by zero
}catch (e: ArithmeticException){ // 산술 연산에 대한 예외 처리
println("Exception is handled. ${e.message}")
}finally {
println("finally 블록은 항상 반드시 실행됨.")
}
}
Exception is handled. / by zero
finally 블록은 항상 반드시 실행됨.
fun main() {
val a = 6
val b = 0
val c: Int
try{
c = a / b // Division by zero
}catch (e: Exception){
e.printStackTrace() // 스택 추적하기
}finally {
println("finally 블록은 항상 반드시 실행됨.")
}
}
java.lang.ArithmeticException: / by zero
at chap05.section4.TryCatchKt.main(TryCatch.kt:9)
at chap05.section4.TryCatchKt.main(TryCatch.kt)
finally 블록은 항상 반드시 실행됨.
package chap05.section4
fun main() {
var amount = 600
try{
amount -= 100
checkAmount(amount)
}catch (e: Exception){
println(e.message)
}
println("amount: $amount")
}
fun checkAmount(amount: Int) {
if(amount < 1000){
throw Exception("잔고가 ${amount}으로, 1000 이하입니다.")
}
}
잔고가 500으로, 1000 이하입니다.
amount: 500