1. If

fun getGrade(score : Int) : String {
  return if (score >= 90) {
    "A"
  } else if (score >= 80) {
    "B"
  } else {
    "C"
  }
}

fun validateScore(score : Int) : String {
  if (score !in 0...100) {
    throw IllegalArgumentException("not valid score range")
  }
}

삼항연산자

  • 코틀린에는 삼항연산자가 존재하지 않는다
  • if식의 return을 사용해 상쇄할 수 있다
fun renamePackage(fullName : String, newName : String) : String {
  val i = fullName.lastIndexOf('.')
  val prefix = if (i >= 0) fullName.substring(0. i+1) else return newName
  return prefix + newName
}

2. When

  • Kotlin에서는 Switch-case가 사라지고 대신에 When이 생김
  • 자바의 switch-case문과는 달리 풀스루(full-through)를 하지 않는다
    • 코틀린은 조건을 만족하는 것만 실행한다
fun getGrade(score : Int) : String {
  return when (score/10) {
    9 -> "A"
    8 -> "B"
    else -> "C"
  }
}

fun getGrade2(score : Int) : String =
  when (score) {
    in 90...100 -> "A"
    80...89 -> "B"
    77 -> "Lucky"
    else -> "C"
  }

3. For

  • in 사용
  • 범위
    • 1..3 : 1 <= x <= 3
    • 1 until 3 : 1 <= x < 3
for (i in 1..3) {
  println(i)
}

for (i in 1..5 step 2) {
  println(i)
}

for (i in 3 downTo 1) {
  println(i)
}

for (i in 5 downTo 1 step 2) {
  println(i)
}

val numbers = listOf(1, 2, 3)
for (number in numbers) {
  println(number)
}

Exception

  • 코틀린에서는 자바와 달리 Check ExceptionUnchecked Exception을 구분하지 않는다

    try-catch

fun parseToInt(str : String) : Int {
  try {
    return str.toInt() 
  } catch (e : NumberFormatException) {
      throw NumberFormatException("주어진 ${str}은 숫자가 아닙니다")
  } finally {
    println("finished method")
  }
}

try-with-resource

  • try-with-resource가 사라지고 use 사용
public void readFile(String path) throws IOExcepion {
  try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
    System.out.println(reader.readLine());
  }
}

↓↓↓↓↓

fun readFile(path : String) {
  BufferedReader(FileReader(path)).use { reader ->
    println(reader.readLine())
  }
}
  • Checked Exception
    • Kotlin은 Checked Exception, Unchecked Exception 구분이 없고 모두 Unchecked Exception 이다

Function

  • default parameter, named parameter 사용 가능
  • 단, 자바 메서드에는 named parameter를 적용할 수 없다
fun repeat(str : String, num : Int = 3, newLine : Boolean = true) {
  for (1..n) {
    if (newline) {
      println(str)
    }
    print(str)
  }
}

repeat("hi", 2, true)
repeat("hi", 2)
repeat("hi")
repeat("hi", newLine=false)
  • Unit : 자바의 void 타입
    • 함수 정의에서 return 타입을 지정하지 않으면 코틀린은 Unit 함수를 정의한다고 가정

가변인자 (vararg)

  • vararg 사용
public static void printAll(String... strings) {
  for (String string : strings) {
    System.out.println(string);
  }
}

↓↓↓↓↓

func printAll(vararg strings : String) {
  for (str in strings) {
    println(str)
  }
}

확장함수

  • 기존 클래스를 확장하여 새로운 함수를 추가해서 사용한다
  • 마치 확장 함수를 기존 클래스 안에 존재하는 멤버 함수 처럼 사용 가능하다
  • 확장함수에서는 클래스에 있는 private, protected 멤버를 가져올 수 없다 (캡슐화가 깨지지 않게 하기 위해)
  • 멤버 함수와 확장 함수의 이름이 동일한 경우 멤버 함수가 호출된다

  • String의 확장 함수 생성
fun main() {
  val str = "ABC"
  
  // 확장함수 사용
  println(str.lastChar())   // C

}

fun String.lastChar() : Char {
  return this[this.length - 1]
}
  • this : 수신객체
  • String : 수신객체 타입

  • 확장 함수가 오버라이드 된 경우에는 해당 변수의 현재 타입의 확장 함수가 호출된다
val train : Train = Train()
train.isExpensive()   // Train의 확장 함수

val train2 : Train = Ktx()
train.isExpensive()   // Train의 확장 함수

val train3 : Ktx = Ktx()
ktx.isExpensive()   // Ktx의 확장 함수
  • 확장 프로퍼티
    • 확장 함수처럼 프로퍼티로도 확장이 가능하다
val String.lastChar : Char 
  get() = this[this.length - 1]

중위 함수(infix)

  • 코틀린에서 함수를 호출하는 새로운 방법
  • 변수.함수이름(argument) 대신 변수 함수이름 argument로 호출
  • infix 키워드 사용
// 전통적인 함수
fun Int.add(other : int) {
  return this + other
}

// infix 함수
infix fun Int.add2(other : int) {
  return this + other
}

fun main() {
  3.add(4)    // 7

  3.add2(4)   // 7
  3 add2 4    // 7
}

인라인 함수(inline)

  • 함수가 호출되는 대신 함수가 호출되는 지점에 함수 내용이 그대로 삽입된다
  • 함수를 파라미터로 전달할 때에 오버헤드를 줄일 수 있지만 성능저하를 유발할 수 있다

람다식

  • 자바에서는 함수의 파라미터로 함수를 넘길 수 없었다
    • Functional inferface를 사용해 함수로 넘기는 것 처럼만 사용 되었다
  • 코틀린에서는 파라미터로 함수를 넣을 수 있다
  • 람다식이 여러 줄일 경우 마지막 줄이 자동으로 return 값이 된다 (return 키워드를 사용하지 않아도 됨)
fun main() {
  val fruits = listOf(
    Fruit("사과", 1_000),
    Fruit("바나나", 2_000)
  )

  val isApple = { fruit : Fruit -> fruit.name == "사과"}
  filterFruits(fruits, isApple)
}

private fun filterFruits(
  fruits: List<Fruit>,
  filter: (Fruit) -> Boolean 
) : List<Fruit> {
  return fruit.filter(filter)  // List 내장함수
}
  • 함수 파라미터로 함수를 넣을 때 축약도 가능하다
    • return 타입이 추론 가능한경우 생략 가능
      filterFruits(fruits, { fruit -> fruit.name == "사과"})
      
    • 파라미터로 함수가 마지막 자리에 들어간다면 소괄호 밖으로 함수 파라미터를 뺄 수 있다
      filterFruits(fruits) { fruit -> fruit.name == "사과"}
      
    • 파라미터가 1개인 경우 it으로 사용 가능하다
      filterFruits(fruits) { it.name == "사과"}