Notice
Recent Posts
Recent Comments
Link
250x250
반응형
«   2025/12   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
Archives
Today
Total
관리 메뉴

백고등어 개발 블로그

코틀린 강의 5강: 클래스와 객체 본문

코틀린 강의

코틀린 강의 5강: 클래스와 객체

백고등어 2025. 10. 28. 17:38
728x90
반응형

5강: 클래스와 객체

클래스 선언

코틀린에서 클래스는 class 키워드로 선언합니다.

기본 클래스

class Person {
    var name: String = ""
    var age: Int = 0
    
    fun introduce() {
        println("안녕하세요, 저는 $name이고 ${age}세입니다.")
    }
}

fun main() {
    val person = Person()
    person.name = "김코틀린"
    person.age = 25
    person.introduce()  // 안녕하세요, 저는 김코틀린이고 25세입니다.
}

생성자

주 생성자(Primary Constructor)

클래스 헤더에 선언하는 생성자입니다:

class Person(val name: String, val age: Int) {
    fun introduce() {
        println("안녕하세요, 저는 $name이고 ${age}세입니다.")
    }
}

fun main() {
    val person = Person("김코틀린", 25)
    person.introduce()
}

init 블록에서 초기화 로직을 작성할 수 있습니다:

class Person(val name: String, val age: Int) {
    init {
        println("Person 객체가 생성되었습니다: $name")
    }
    
    init {
        require(age >= 0) { "나이는 0 이상이어야 합니다" }
    }
}

부 생성자(Secondary Constructor)

constructor 키워드로 추가 생성자를 정의할 수 있습니다:

class Person(val name: String) {
    var age: Int = 0
    
    constructor(name: String, age: Int) : this(name) {
        this.age = age
    }
    
    fun introduce() {
        println("이름: $name, 나이: $age")
    }
}

fun main() {
    val person1 = Person("김코틀린")
    val person2 = Person("이자바", 30)
    
    person1.introduce()  // 이름: 김코틀린, 나이: 0
    person2.introduce()  // 이름: 이자바, 나이: 30
}

프로퍼티

getter와 setter

코틀린의 프로퍼티는 자동으로 getter와 setter를 제공합니다:

class Rectangle(var width: Int, var height: Int) {
    // 커스텀 getter
    val area: Int
        get() = width * height
    
    // 커스텀 setter
    var size: Int
        get() = width * height
        set(value) {
            // 정사각형으로 만들기
            val side = kotlin.math.sqrt(value.toDouble()).toInt()
            width = side
            height = side
        }
}

fun main() {
    val rect = Rectangle(5, 10)
    println("면적: ${rect.area}")  // 50
    
    rect.size = 100
    println("너비: ${rect.width}, 높이: ${rect.height}")  // 10, 10
}

지연 초기화(Late Initialization)

lateinit 키워드로 나중에 초기화할 프로퍼티를 선언할 수 있습니다:

class MyTest {
    lateinit var testData: String
    
    fun setup() {
        testData = "테스트 데이터"
    }
    
    fun test() {
        if (::testData.isInitialized) {
            println(testData)
        }
    }
}

지연 계산(Lazy Initialization)

lazy 위임을 사용하면 처음 사용될 때 초기화됩니다:

class DataLoader {
    val heavyData: String by lazy {
        println("데이터 로딩 중...")
        "무거운 데이터"
    }
}

fun main() {
    val loader = DataLoader()
    println("객체 생성 완료")
    println(loader.heavyData)  // 이 시점에 "데이터 로딩 중..." 출력
    println(loader.heavyData)  // 캐시된 값 사용
}

데이터 클래스(Data Class)

데이터를 담는 클래스를 쉽게 만들 수 있습니다:

data class User(val name: String, val age: Int, val email: String)

fun main() {
    val user1 = User("김코틀린", 25, "kotlin@example.com")
    val user2 = User("김코틀린", 25, "kotlin@example.com")
    
    // 자동 생성된 toString()
    println(user1)  // User(name=김코틀린, age=25, email=kotlin@example.com)
    
    // 자동 생성된 equals()
    println(user1 == user2)  // true
    
    // 자동 생성된 copy()
    val user3 = user1.copy(age = 26)
    println(user3)  // User(name=김코틀린, age=26, email=kotlin@example.com)
    
    // 구조 분해 선언
    val (name, age, email) = user1
    println("이름: $name, 나이: $age")
}

데이터 클래스는 다음을 자동으로 생성합니다:

  • equals() / hashCode()
  • toString()
  • copy()
  • componentN() 함수들

상속

코틀린의 모든 클래스는 기본적으로 final입니다. 상속을 허용하려면 open 키워드를 사용해야 합니다:

open class Animal(val name: String) {
    open fun makeSound() {
        println("동물 소리")
    }
}

class Dog(name: String) : Animal(name) {
    override fun makeSound() {
        println("멍멍!")
    }
}

class Cat(name: String) : Animal(name) {
    override fun makeSound() {
        println("야옹!")
    }
}

fun main() {
    val dog = Dog("바둑이")
    val cat = Cat("나비")
    
    dog.makeSound()  // 멍멍!
    cat.makeSound()  // 야옹!
}

추상 클래스(Abstract Class)

abstract class Shape {
    abstract val area: Double
    abstract fun draw()
    
    // 일반 함수도 포함 가능
    fun describe() {
        println("이 도형의 면적은 $area입니다.")
    }
}

class Circle(val radius: Double) : Shape() {
    override val area: Double
        get() = Math.PI * radius * radius
    
    override fun draw() {
        println("원을 그립니다. 반지름: $radius")
    }
}

fun main() {
    val circle = Circle(5.0)
    circle.draw()
    circle.describe()
}

인터페이스(Interface)

interface Drawable {
    fun draw()
    
    // 기본 구현 제공 가능
    fun description() {
        println("그릴 수 있는 객체입니다.")
    }
}

interface Clickable {
    fun click()
    fun showClick() = println("클릭됨")  // 기본 구현
}

class Button : Drawable, Clickable {
    override fun draw() {
        println("버튼을 그립니다.")
    }
    
    override fun click() {
        println("버튼이 클릭되었습니다!")
    }
}

fun main() {
    val button = Button()
    button.draw()
    button.click()
    button.description()
}

가시성 제한자(Visibility Modifiers)

  • public: 모든 곳에서 접근 가능 (기본값)
  • private: 클래스 내부에서만 접근 가능
  • protected: 클래스와 하위 클래스에서 접근 가능
  • internal: 같은 모듈 내에서만 접근 가능
open class BankAccount {
    private var balance: Double = 0.0
    protected var accountNumber: String = ""
    
    fun deposit(amount: Double) {
        balance += amount
    }
    
    fun getBalance(): Double = balance
}

class SavingsAccount : BankAccount() {
    fun setAccountNumber(number: String) {
        accountNumber = number  // protected 접근 가능
    }
}

중첩 클래스와 내부 클래스

중첩 클래스(Nested Class)

class Outer {
    private val outerProperty = "외부 클래스"
    
    class Nested {
        fun hello() {
            println("중첩 클래스")
            // outerProperty에 접근 불가
        }
    }
}

fun main() {
    val nested = Outer.Nested()
    nested.hello()
}

내부 클래스(Inner Class)

inner 키워드를 사용하면 외부 클래스의 멤버에 접근할 수 있습니다:

class Outer {
    private val outerProperty = "외부 클래스"
    
    inner class Inner {
        fun hello() {
            println("내부 클래스")
            println(outerProperty)  // 접근 가능
        }
    }
}

fun main() {
    val inner = Outer().Inner()
    inner.hello()
}

싱글톤 패턴(Object Declaration)

object 키워드로 싱글톤을 쉽게 만들 수 있습니다:

object DatabaseConnection {
    private var connectionCount = 0
    
    fun connect() {
        connectionCount++
        println("연결됨. 총 연결 수: $connectionCount")
    }
    
    fun getConnectionCount() = connectionCount
}

fun main() {
    DatabaseConnection.connect()
    DatabaseConnection.connect()
    println("총 연결: ${DatabaseConnection.getConnectionCount()}")
}

동반 객체(Companion Object)

클래스 내부에서 정적 멤버처럼 사용할 수 있습니다:

class User private constructor(val name: String) {
    companion object {
        private var userCount = 0
        
        fun create(name: String): User {
            userCount++
            return User(name)
        }
        
        fun getUserCount() = userCount
    }
}

fun main() {
    val user1 = User.create("김코틀린")
    val user2 = User.create("이자바")
    println("생성된 사용자 수: ${User.getUserCount()}")
}

마치며

이번 강의에서는 코틀린의 클래스와 객체지향 프로그래밍에 대해 알아보았습니다. 데이터 클래스로 간결하게 데이터 모델을 만들 수 있고, object 선언으로 싱글톤을 쉽게 구현할 수 있다는 점이 코틀린의 큰 장점입니다. 다음 강의에서는 컬렉션에 대해 알아보겠습니다.

728x90
반응형