개발/Kotlin

[Kotlin In Action] 2. μ½”ν‹€λ¦° 기초

도리 🐟 2021. 5. 10. 00:24

#1. ν•¨μˆ˜

Hello, world!

fun main(args: Array<String>) {
  println("Hello, world!")
}

  • ν•¨μˆ˜λ₯Ό μ„ μ–Έν•  λ•Œ fun ν‚€μ›Œλ“œλ₯Ό μ‚¬μš©ν•œλ‹€.
  • λ³€μˆ˜λ₯Ό μ„ μ–Έν•  λ•Œλ‚˜ νŒŒλΌλ―Έν„° 이름 뒀에 νƒ€μž…μ„ μ“΄λ‹€.
  • ν•¨μˆ˜λ₯Ό μ΅œμƒμœ„ μˆ˜μ€€μ— μ •μ˜ν•  수 μžˆλ‹€. 클래슀 μ•ˆμ— λ„£μ–΄μ•Ό ν•  ν•„μš”κ°€ μ—†λ‹€.
  • 배열도 일반적인 ν΄λž˜μŠ€μ™€ λ§ˆμ°¬κ°€μ§€λ‹€.
  • μ„Έλ―Έμ½œλ‘ (;)을 뢙이지 μ•Šμ•„λ„ λœλ‹€.

statement(λ¬Έ)κ³Ό expression(식)의 ꡬ뢄

  • 식: 값을 λ§Œλ“€μ–΄λ‚΄λ©°, λ‹€λ₯Έ μ‹μ˜ ν•˜μœ„μš”μ†Œλ‘œ 계산에 μ°Έμ—¬ν•  수 μžˆλ‹€.
  • λ¬Έ: μžμ‹ μ„ λ‘˜λŸ¬μ‹Έκ³  μžˆλŠ” κ°€μž₯ μ•ˆμͺ½ λΈ”λŸ­μ˜ μ΅œμƒμœ„ μš”μ†Œλ‘œ μ‘΄μž¬ν•˜μ—¬ μ•„λ¬΄λŸ° 값을 λ§Œλ“€μ–΄λ‚΄μ§€ μ•ŠλŠ”λ‹€.
μžλ°”μ—μ„œλŠ” λͺ¨λ“  μ œμ–΄κ΅¬μ‘°κ°€ 'λ¬Έ'인 반면, μ½”ν‹€λ¦°μ—μ„œλŠ” 루프λ₯Ό μ œμ™Έν•œ λŒ€λΆ€λΆ„μ˜ μ œμ–΄κ΅¬μ‘°κ°€ '식'이닀.

식이 본문인 ν•¨μˆ˜ (expression body function)

// λΈ”λŸ­μ΄ 본문인 ν•¨μˆ˜
fun max(a: Int, b: Int): Int { 
  return if (a > b) a else b
}

// 식이 본문인 ν•¨μˆ˜ 
fun max(a: Int, b: Int): Int = if (a > b) a else b
// λ°˜ν™˜ νƒ€μž… μƒλž΅λ„ κ°€λŠ₯ν•˜λ‹€.
fun max(a: Int, b: Int) = if (a > b) a else b

식이 본문인 ν•¨μˆ˜λŠ” λ°˜ν™˜ νƒ€μž…μ„ 적지 μ•Šμ•„λ„ μ»΄νŒŒμΌλŸ¬κ°€ ν•¨μˆ˜ λ³Έλ¬Έ 식을 λΆ„μ„ν•˜μ—¬ ν•¨μˆ˜ λ°˜ν™˜ νƒ€μž…μ„ μ •ν•΄ μ€€λ‹€.

-> νƒ€μž… μΆ”λ‘ 

#2. λ³€μˆ˜

val question = "μ‚Ά, 우주, 그리고 λͺ¨λ“  것에 λŒ€ν•œ ꢁ극적인 질문"

val answer = 42 //Int νƒ€μž…μœΌλ‘œ νƒ€μž… μΆ”λ‘ 
val yearToCompute = 7.5e6 //Double νƒ€μž…μœΌλ‘œ νƒ€μž… μΆ”λ‘ 

μ΄ˆκΈ°ν™” 식을 μ‚¬μš©ν•˜μ§€ μ•Šκ³  λ³€μˆ˜λ₯Ό μ„ μ–Έν•˜λ €λ©΄ λ³€μˆ˜ νƒ€μž…μ„ λ°˜λ“œμ‹œ λͺ…μ‹œν•΄μ•Ό ν•œλ‹€.

val answer: Int answer = 42

λ³€κ²½ κ°€λŠ₯ν•œ / λ³€κ²½ λΆˆκ°€λŠ₯ν•œ λ³€μˆ˜

  • val (value): λ³€κ²½ λΆˆκ°€λŠ₯ν•œ μ°Έμ‘°λ₯Ό μ €μž₯ν•˜λŠ” λ³€μˆ˜ - μžλ°”μ˜ final λ³€μˆ˜
  • var (variable): λ³€κ²½ κ°€λŠ₯ν•œ μ°Έμ‘° - μžλ°”μ˜ 일반 λ³€μˆ˜

 

κΈ°λ³Έμ μœΌλ‘œλŠ” λͺ¨λ“  λ³€μˆ˜λ₯Ό val둜 μ„ μ–Έν•˜κ³ , λ‚˜μ€‘μ— κΌ­ ν•„μš”ν•  λ•Œμ—λ§Œ var둜 λ³€κ²½ν•˜λŠ” 것이 μ’‹λ‹€.

 

val λ³€μˆ˜λŠ” ν•œ 번만 μ΄ˆκΈ°ν™”λΌμ•Ό ν•œλ‹€.
쑰건에 따라 val κ°’을 λ‹€λ₯Έ μ—¬λŸ¬ κ°’μœΌλ‘œ μ΄ˆκΈ°ν™”ν•  μˆ˜λ„ μžˆλ‹€.

val message: String
if (canPerformOperation()) {
  message = "Success"
} else {
  message = "Failed"
}

 

β­‘ val μ°Έμ‘° μžμ²΄λŠ” λΆˆλ³€μΌμ§€λΌλ„, κ·Έ μ°Έμ‘°κ°€ κ°€λ¦¬ν‚€λŠ” 객체 λ‚΄λΆ€ 값은 변경될 수 μžˆλ‹€.

val languages = arrayListOf("Java") //λΆˆλ³€ μ°Έμ‘° μ„ μ–Έ
language.add("Kotlin") //μ°Έμ‘°κ°€ κ°€λ¦¬ν‚€λŠ” 객체 λ‚΄λΆ€ λ³€κ²½ 

 

var λ³€μˆ˜μ˜ 'κ°’'은 λ³€κ²½ν•  수 μžˆμ§€λ§Œ λ³€μˆ˜μ˜ 'νƒ€μž…'은 κ³ μ •λœλ‹€.

var answer = 42 answer = "no answer" //Error: type mismatch

#3. λ¬Έμžμ—΄ ν…œν”Œλ¦Ώ

fun main(args: Array<String>) {
  val name = if (args.size > 0) args[0] else "Kotlin"
  println("Hello, $name!")
}

=> μžλ°”μ˜ ("Hello" + name + "!")κ³Ό λ™μΌν•œ κΈ°λŠ₯

λ³΅μž‘ν•œ 식은 {}둜 λ‘˜λŸ¬μ‹Έμ„œ λ¬Έμžμ—΄ ν…œν”Œλ¦Ώ μ•ˆμ— 넣을 수 μžˆλ‹€.

fun main(args: Array<String>) {
  if (args.size > 0) {
    println("Hello, ${args[0]}!")
  }
}

#4. ν”„λ‘œνΌν‹°

μžλ°”μ—μ„œλŠ” 데이터λ₯Ό ν•„λ“œμ— μ €μž₯ν•˜λ©°, ν•„λ“œμ˜ κ°€μ‹œμ„±μ€ κΈ°λ³Έ λΉ„κ³΅κ°œ(private)이닀.
데이터에 μ ‘κ·Όν•˜λŠ” ν†΅λ‘œλ‘œ μ“Έ 수 μžˆλŠ” μ ‘κ·Όμž λ©”μ„œλ“œ(보톡 Getter)λ₯Ό μ œκ³΅ν•˜κ³ , ν•„λ“œλ₯Ό λ³€κ²½ν•  Setterλ₯Ό μΆ”κ°€ μ œκ³΅ν•  수 μžˆλ‹€.

 

μžλ°”μ—μ„œλŠ” ν•„λ“œμ™€ μ ‘κ·Όμžλ₯Ό λ¬Άμ–΄ ν”„λ‘œνΌν‹°λΌκ³  λΆ€λ₯Έλ‹€.
코틀린은 ν”„λ‘œνΌν‹°λ₯Ό μ–Έμ–΄ κΈ°λ³Έ κΈ°λŠ₯으둜 μ œκ³΅ν•˜λ©°, μžλ°”μ˜ ν•„λ“œμ™€ μ ‘κ·Όμž λ©”μ„œλ“œλ₯Ό μ™„μ „νžˆ λŒ€μ‹ ν•œλ‹€.
ν”„λ‘œνΌν‹° 선언은 valμ΄λ‚˜ var을 μ‚¬μš©ν•œλ‹€.

class Person(
  val name: String, //읽기 μ „μš©, (λΉ„κ³΅κ°œ) ν•„λ“œμ™€ (곡개) Getter 생성됨 
  var isMarried: Boolean //λ³€κ²½ κ°€λŠ₯, (λΉ„κ³΅κ°œ) ν•„λ“œ / (곡개) Getter / (곡개) Setter 생성됨 
)

 

코틀린은 값을 μ €μž₯ν•˜κΈ° μœ„ν•œ λΉ„κ³΅κ°œ ν•„λ“œμ™€ κ·Έ ν•„λ“œμ— λŒ€ν•œ Setter, Getter둜 이뀄진 κ°„λ‹¨ν•œ λ””ν΄νŠΈ μ ‘κ·Όμž κ΅¬ν˜„μ„ μ œκ³΅ν•œλ‹€.

val person = Person("Bob", true) //new ν‚€μ›Œλ“œμ—†μ΄ μƒμ„±μž 호좜
println(person.name) //ν”„λ‘œνΌν‹° 이름을 직접 μ‚¬μš©ν•΄λ„ 코틀린이 μžλ™μœΌλ‘œ Getterλ₯Ό ν˜ΈμΆœν•œλ‹€.

 

Backing field (λ’·λ°›μΉ¨ν•˜λŠ” ν•„λ“œ)
: ν”„λ‘œνΌν‹°μ˜ 값을 μ €μž₯ν•˜κΈ° μœ„ν•œ ν•„λ“œ

 

ν”„λ‘œνΌν‹°μ˜ 값을 κ·Έλ•Œκ·Έλ•Œ κ³„μ‚°ν•˜κΈ° μ›ν•œλ‹€λ©΄ Custom Getterλ₯Ό μž‘μ„±ν•˜λ©΄ λœλ‹€.

μ»€μŠ€ν…€ μ ‘κ·Όμž

class Rectangle(val height: Int, val width: Int) {
  val isSquare: Boolean
    get() { //ν”„λ‘œνΌν‹° Getter μ„ μ–Έ 
      return height = width 
    }
}

-> ν”„λ‘œνΌν‹°μ— μ ‘κ·Όν•  λ•Œλ§ˆλ‹€ Getterκ°€ ν”„λ‘œνΌν‹°μ˜ 값을 맀번 λ‹€μ‹œ κ³„μ‚°ν•œλ‹€.

 

• νŒŒλΌλ―Έν„°κ°€ μ—†λŠ” ν•¨μˆ˜ μ •μ˜ vs. μ»€μŠ€ν…€ Getter μ •μ˜

κ΅¬ν˜„μ΄λ‚˜ μ„±λŠ₯은 λΉ„μŠ·, 차이점은 가독성
일반적으둜 클래슀의 νŠΉμ„±μ„ μ •μ˜ν•˜κ³  μ‹Άλ‹€λ©΄, ν”„λ‘œνΌν‹°λ‘œ μ •μ˜ν•΄μ•Ό ν•œλ‹€.

#5. enum

μ½”ν‹€λ¦°μ—μ„œ enum은 soft keyword -> class μ•žμ— μžˆμ„ λ•Œλ₯Ό μ œμ™Έν•˜λ©΄ λ‹€λ₯Έ 이름에 쓰일 수 μžˆλ‹€.
(cf. keyword: λ³€μˆ˜ λ“± 이름에 μ‚¬μš© λΆˆκ°€λŠ₯, 예 - class)

enum class Color(
  val r: Int, val g: Int, val b: Int
) {
  RED(255, 0, 0), ORANGE(255, 165, 0),
  YELLOW(255, 255, 0), GREEN(0, 255, 0), BLUE(0, 0, 255),
  INDIGO(75, 0, 130), VIOLET(238, 130, 238); //μ—¬κΈ°μ„œλŠ” λ°˜λ“œμ‹œ μ„Έλ―Έμ½œλ‘ μ„ μ‚¬μš©ν•΄μ•Ό ν•œλ‹€.
  
  fun rgb() = (r * 256 + g) * 256 + b //λ©”μ„œλ“œ μ •μ˜ 
}

fun getMnemonic(color: Color) = 
  when (color) {
    Color.RED -> "Richard"
    Color.ORANGE -> "Of"
    Color.YELLOW -> "York"
    Color.GREEN -> "Gave"
    Color.BLUE -> "Battle"
    Color.INDIGO -> "In"
    Color.VIOLET -> "Vain"
  }

 

  • when : if와 λ§ˆμ°¬κ°€μ§€λ‘œ when도 값을 λ§Œλ“€μ–΄λ‚΄λŠ” '식'이닀.
    • μžλ°”μ™€ 달리 각 λΆ„κΈ°μ˜ 끝에 breakλ₯Ό 넣지 μ•Šμ•„λ„ λœλ‹€.
    • ν•œ λΆ„κΈ° μ•ˆμ—μ„œ μ—¬λŸ¬ 값을 맀치 νŒ¨ν„΄μœΌλ‘œ μ‚¬μš©ν•  μˆ˜λ„ μžˆλ‹€. (κ°’ 사이 ',' μ‚¬μš©)
    • 인자둜 아무 κ°μ²΄λ‚˜ 올 수 μžˆλ‹€.
    • μΈμžμ—†μ΄ ν˜ΈμΆœν•  μˆ˜λ„ μžˆλ‹€.

#6. 슀마트 캐슀트: νƒ€μž… 검사와 νƒ€μž… 캐슀트λ₯Ό μ‘°ν•©

예제: λ§μ…ˆ μ‚°μˆ μ‹ 계산
- ν•¨μˆ˜ 식을 트리ꡬ쑰둜 μ €μž₯
- μ–΄λ–€ 식이 수라면 κ·Έ 값을 λ°˜ν™˜ν•œλ‹€.
- μ–΄λ–€ 식이 합계라면 μ’Œν•­κ³Ό μš°ν•­μ˜ 값을 κ³„μ‚°ν•œ λ‹€μŒ κ·Έ 두 값을 ν•©ν•œ 값을 λ°˜ν™˜ν•œλ‹€.
interface Expr
class Num(val value: Int) : Expr
class Sum(val left: Expr, val right: Expr) : Expr

 

μ½”ν‹€λ¦°μ—μ„œλŠ” isλ₯Ό μ‚¬μš©ν•΄ λ³€μˆ˜ νƒ€μž…μ„ κ²€μ‚¬ν•œλ‹€.
is둜 κ²€μ‚¬ν•˜λ©΄ μ»΄νŒŒμΌλŸ¬κ°€ μΊμŠ€νŒ… ν•΄μ€€λ‹€. -> 슀마트 캐슀트

  • 슀마트 캐슀트λ₯Ό μ‚¬μš©ν•˜λ €λ©΄ val이어야 ν•œλ‹€.
  • λͺ…μ‹œμ μœΌλ‘œ νƒ€μž… μΊμŠ€νŒ…ν•˜λ €λ©΄ as ν‚€μ›Œλ“œλ₯Ό μ‚¬μš©ν•œλ‹€.

if μ‚¬μš©

fun eval(e: Expr): Int = 
  if (e is Num) {
    e.value //if 뢄기에 λΈ”λŸ­μ„ μ‚¬μš©ν•˜λŠ” 경우, κ·Έ λΈ”λŸ­μ˜ λ§ˆμ§€λ§‰ 식이 κ·Έ λΆ„κΈ°μ˜ κ²°κ³Ό 값이닀.
  } else if (e is Sum) {
    eval(e.right) + eval(e.left)
  } else {
    throw IllegalArgumentException("Unknown expression")
  }

if λŒ€μ‹  when μ‚¬μš©

fun eval(e: Expr): Int = 
  when (e) {
    is Num -> e.value
    is Sum -> eval(e.right) + eval(e.left)
    else -> throw IllegalArgumentException("Unknown expression")
  }
  • λΈ”λŸ­μ˜ λ§ˆμ§€λ§‰ 식이 λΈ”λŸ­μ˜ κ²°κ³Ό κ·œμΉ™
    • ν•¨μˆ˜μ— λŒ€ν•΄μ„œλŠ” μ„±λ¦½ν•˜μ§€ μ•ŠλŠ”λ‹€.
    • 식이 본문인 ν•¨μˆ˜λŠ” λΈ”λŸ­μ„ 본문으둜 κ°€μ§ˆ 수 μ—†κ³ , λΈ”λŸ­μ΄ 본문인 ν•¨μˆ˜λŠ” 내뢀에 return문이 λ°˜λ“œμ‹œ μžˆμ–΄μ•Ό ν•œλ‹€.

#7. μ΄ν„°λ ˆμ΄μ…˜

while 루프

while, do-while -> μžλ°”μ™€ κ°™λ‹€.

while (쑰건) {
  ...
}

do {
  ...
} while (쑰건)

μˆ˜μ— λŒ€ν•œ μ΄ν„°λ ˆμ΄μ…˜

  • λ²”μœ„ (range): 두 κ°’μœΌλ‘œ 이루어진 ꡬ간
    • .. μ—°μ‚°μžλ‘œ μ‹œμž‘ κ°’κ³Ό 끝 값을 μ—°κ²°ν•΄μ„œ λ²”μœ„λ₯Ό λ§Œλ“ λ‹€. (폐ꡬ간)
    • 문자 νƒ€μž…μ˜ 값에도 μ μš©ν•  수 μžˆλ‹€.
val oneToTen = 1..10

 

for (i in 100 downTo 1 step 2) { //100λΆ€ν„° 1κΉŒμ§€ 2μ”© 쀄여가며 반볡 
  ...
}

끝 값을 ν¬ν•¨ν•˜μ§€ μ•ŠλŠ” λ²”μœ„ -> until μ‚¬μš©

(x in until size)
//equals
(x in 0..size-1)

맡에 λŒ€ν•œ μ΄ν„°λ ˆμ΄μ…˜

val binaryReps = TreeMap<Char, String>()

for (c in 'A'..'F') {
    val binary = Integer.toBinaryString(c.toInt())
    binaryReps[c] = binary
}

for ((letter, binary) in binaryReps) { //맡의 킀와 값을 두 λ³€μˆ˜μ— 각각 λŒ€μž…ν•œλ‹€. - ꡬ쑰 λΆ„ν•΄ ꡬ문
    println("$letter = $binary")
}

in으둜 μ»¬λ ‰μ…˜μ΄λ‚˜ λ²”μœ„μ˜ μ›μ†Œ 검사

  • in: μ–΄λ–€ 값이 λ²”μœ„μ— μ†ν•˜λŠ”μ§€ 검사
  • !in: λ²”μœ„μ— μ†ν•˜μ§€ μ•ŠλŠ”μ§€ 검사

#8. μ½”ν‹€λ¦°μ˜ μ˜ˆμ™Έ 처리

fun readNumber(reader: BufferedReader): Int? { //throws λͺ…μ‹œν•  ν•„μš”κ°€ μ—†λ‹€.
    try {
        val line = reader.readLine()
        return Integer.parseInt(line)
    }
    catch (e: NumberFormatException) {
        return null
}
finally {
        reader.close()
    }
}

코틀린은 체크 μ˜ˆμ™Έμ™€ 언체크 μ˜ˆμ™Έλ₯Ό κ΅¬λ³„ν•˜μ§€ μ•ŠλŠ”λ‹€.
try도 '식'μ΄λ―€λ‘œ, try 값을 λ³€μˆ˜μ— λŒ€μž…ν•  수 μžˆλ‹€. λ§ˆμ§€λ§‰ μ‹μ˜ 값이 전체 κ²°κ³Ό 값이닀.
if와 달리 tryλŠ” 항상 {}둜 감싸야 ν•œλ‹€.