본문 바로가기
코틀린/개발자를 위한 코틀린 프로그래밍

[chapter05] 클래스 알아보기

by 측면삼각근 2023. 8. 13.
728x90
반응형

들어가기 전에

본 포스팅은 개발자를 위한 코틀린 프로그래밍 의 chapter단위로 공부하고, 정리, 부족한 내용의 추가 학습내용을 정리하는 블로깅입니다.

이전 포스팅 ⬇️

[chapter04] 함수 알아보기

 

[chapter04] 함수 알아보기

들어가기 전에 본 포스팅은 개발자를 위한 코틀린 프로그래밍 의 chapter단위로 공부하고, 정리, 부족한 내용의 추가 학습내용을 정리하는 블로깅입니다. 이전 포스팅 ⬇️ [chapter03] 문장 제어처

messycode.tistory.com


01 클래스 알아보기

기본 클래스에 대한 내용은 책의 내용을 정리하는 것보다, 공식문서의 내용이 더 간략하며 이해하기 쉽다고 생각되어, 공식문서 내용으로 대체합니다.

[코틀린 공식문서] Classes; 클래스

 

[코틀린 공식문서] Classes; 클래스

들어가기 전에 해당 블로깅은 코틀린 공식문서의 Classes를 번역하며 학습한 내용입니다. 학습 중임에 따라 이해하는데 도움이 되는 부분들을 추가되고 의역된 부분이 있습니다. 혹시 잘못된 설

messycode.tistory.com

02 상속 알아보기

상속에 대한 내용 또한 책의 내용보다 공식문서의 내용이 더 직관적이라 공식문서로 대체합니다.

[코틀린 공식문서] Inheritance; 상속

 

[코틀린 공식문서] Inheritance; 상속

들어가기 전에 해당 블로깅은 코틀린 공식문서의 Inheritance를 번역하며 학습한 내용입니다. 학습 중임에 따라 이해하는데 도움이 되는 부분들을 추가되고 의역된 부분이 있습니다. 혹시 잘못된

messycode.tistory.com

03 다양한 클래스 알아보기

3.1 내포 클래스, 이너 클래스, 지역 클래스

  • 내포 클래스(Nested class): 외부와 내부 클래스가 별도의 기능을 처리하는 경우
  • 이너 클래스(Inner class): 외부와 내부 클래스가 연관된 처리를 하는 경우
  • 지역 클래스(local class): 함수 내부에도 클래스를 선언하고 사용할 수 있음

내포 클래스(Nested class)

  • 아무런 지시자도 추가하지 않고 정의한다. 클래스 내부에 정의되었지만, 별도의 클래스이다.
  • 내포클래스에서 외부에 사용할 때는 외부 클래스 이름으로 접근해서 사용한다.
    내포 클래스는 자신을 정의한 외부 클래스 내의 속성 등에 직접 접근할 수 없다.
  • 내포 클래스의 객체를 생성할 때는 외부 클래스 이름으로 접근해서 내포 클래스 생성자를 호출해 객체를 만든다.
    그 후 내보에 있는 메서드를 호출해서 처리한다.
class Outer {
    private val bar: Int = 1 // 외부 클래스의 비공개 속성

    class Nested {
        private val nestVar = 100 // 중첩 클래스의 비공개 속성
        fun foo() = 999 // 중첩 클래스의 공개 함수

        fun accessOuter() {
            // 중첩 클래스는 외부 클래스의 비공개 속성에 접근할 수 없다.
            // println(bar)
            // this@Outer.bar // Unresolved reference: @Outer
        }
    }
}

fun main() {
    val nested = Outer.Nested()
    println(nested.foo())
    // println(nested.nestVar) // Cannot access 'nestVar': it is private in 'Nested'
}

이너클래스(inner class)

  • 클래스 내부에 클래스를 정의 할정의할 때 inner 예약어를 붙여서 이너 클래스를 정의할 수 있다.
  • 이너 클래스의 객체는 this이고 외부 클래스의 객체는 this@외부 클래스 이름으로 사용한다.
    그래서 이너 클래스 내부에서 외부 클래스의 속성에 접근할 수 있다.
  • 외부 클래스에서 이너 클래스를 사용하려면 객체를 생성해서 사용한다.
class Outer {
    private val bar: Int = 1 // 외부 클래스의 비공개 속성

    inner class Inner {
        private val bar = 100 // 내부 클래스의 비공개 속성 (동일 이름)
        fun foo() = this@Outer.bar // 내부 클래스의 메서드에서 외부 클래스의 속성에 접근
        fun fbar() = this.bar // 내부 클래스의 메서드에서 내부 클래스의 속성에 접근
    }
    
    fun getBar() = Inner().fbar()
}

fun main() {
    val foo = Outer().Inner().foo() // == 1
    Outer().getBar() // == 100
}

내포 클래스(Nested class)와 이너 클래스(inner class)의 차이점

  • 내포 클래스는 외부 클래스의 이름으로 접근해서 객체를 생성할 수 있지만, 이너클래스는 멤버처럼 객체로 접근해서 객체를 생성한다.
  • 내포 클래스는 (외부 클래스와 별개의 클래스라) 외부 클래스의 속성을 참조할 수 없지만, 이너 클래스는 외부 클래스의 속성을 참조할 수 있다.

04 object 알아보기

최상위 클래스는 object가 아닌 Any이다. object예약어는 클래스 정의하나의 객체 생성을 동시에 한다. 그래서 object 예약어를 사용하면 하나의 객체만 만들어진다. (싱글턴 객체)

object 예약어는 3가지 경우에 사용된다.

  1. object표현식. 익명 클래스로 특정 클래스 없이 객체를 생성
  2. object선언. 하나의 객체만 만들어서 사용
  3. 동반객체(companion object). 클래스에 정의해서 클래스와 같이 동반해서 사용

4.1 object 표현식(expression)

익명의 클래스로 익명의 객체를 만들 필요가 있을 때 object표현식으로 하나의 객체를 만든다.
일회성 객체를 사용하는 경우는 아래와 같다.

  • 최상위 변수에 바로 객체 할당
  • 함수 내부 또는 메서드 내부에서 지역변수에 객체 할당
  • 함수의 매개변수에 객체 할당
  • 내부함수의 반환값으로 정의해서 외부함수의 지역변수를 내부 메서드에서 참조

지역변수에서 익명 클래스 생성

함수나 메서드 등에서 일회성 객체가 필요한 경우 내부에서 정의하여 바로 사용한다.

  • 함수 내부에 여러 개의 변수를 하나의 목적으로 사용할 때 object 표현식에 묶어서 같이 사용한다.
  • 익명 클래스의 객체이지만, 변수에 할당되어서 다른 객체와 동일하게 속성을 갱신하는 방식으로 처리할 수 있다.
fun getLength(): Double {
    val point = object {
        val x = 2.0
        val y = 3.0

        override fun toString() = "Point($x, $y)" //메서드 재정의
    }
    println(point)
    return Math.sqrt((point.x * point.x + point.y * point.y))
}

fun main() {
    println(getLength())
}
// Point(2.0, 3.0)
// 3.605551275463989

함수의 매개변수에 익명 객체 전달

함수의 인자를 전달할 때 익명 객체로 바로 정의해 전달한다.

interface Personnel {
    val name: String
    val age: Int
}

fun getObject(p: Personnel): Personnel {
    return p
}

fun main() { // 인자로 object표현식으로 생성된 객체 전달
    val p = object : Personnel {
        override val name: String = "홍길동"
        override val age: Int = 30
    }
    println(getObject(p).name)
    println(getObject(p).age)
}

 

메서드의 반환값 사용

interface StrType

class C {
    private fun getObject() = object : StrType {
        val x: String = "내부속성값"
    }

    fun printX() {
        println(getObject().x)
    }
}

fun main() {
    val c = C()
    c.printX() // 객체를 만들어서 출력하면 익명 객체의 속성을 출력
}

클래스와 인터페이스를 상속하는 object 표현식 처리

open class A(x: Int) {
    open val y: Int = x
    open fun display() = y
}

interface Add {
    fun add(x: Int, z: Int): Int
}

val a: Add = object : A(10), Add { //클래스와 인터페이스를 상속하고 객체 변수에 할당
    override val y: Int = super.y // 상속한 클래스를 super로 접근
    override fun add(x: Int, z: Int): Int = x + z
}

4.2 object정의

앞서 4.1은 익명 객체를 만드는 경우이지만, object는 하나의 싱글턴 패턴을 만드는 방법이다.

object 정의하는 규칙

  •  class 예약어 대신 object 예약어를 사용하여 object 이름 정의
  • 클래스와 객체 생성이 정의에서 만들어져서 별도의 생성자가 필요 없다.
  • object정의 내부 상수인 const val가능
  • object 정의를 처음으로 사용할 때 메모리에 로딩된다.
  • object 정의의 이름으로 코드 블록에 정의된 속성이나 메서드를 사용할 수 있다.
  • 클래스 정의도 있어서 클래스 상속과 인터페이스 구현이 가능
  • 내부에 객체와 클래스를 정의할 수 있다.

object 정의와 사용

object Counter {
    private var count = 0

    fun getCount(): Int = count
    fun increment(): Int = ++count
}

fun main() {
    Counter.increment()
    println(Counter.getCount())
}

클래스 상속

object정의도 클래스 정의와 마찬가지로 다른 클래스를 상속해서 구현할 수 있다.

open class Value(open val x:Int, open val y:Int)

object Operation: Value(100, 200){
    override val x = super.x
    override val y = super.y
    
    fun add() = x + y
    fun sub() = x - y
    fun mul() = x * y
    fun div() = x / y
}

fun main() {
    println(Operation.add())
    println(Operation.sub())
    println(Operation.mul())
    println(Operation.div())
}

또한 마찬가지로 인터페이스를 구현 할 수 있다.

4.3 동반 객체(companion object)처리

클래스 내부의 Object 정의는 실제 클래스와 상관 없이 작동하는 객체를 만드는 것이다. 하지만 클래스와 상호작용을 하는 동반 객체도 정의할 수 있다.

object vs companion object

  • 클래스와 동반 객체는 하나처럼 움직이도록 구성되었다. 그래서 클래스에 동반 객체를 정의하면 다양한 기능을 클래스 이름으로 처리 할 수 있다.
  • 클래스로 여러 객체를 만들때, 클래스 내부의 동반객체는 하나만 만들어진다.
    그래서 클래스의 여러 객체에서 동반객체는 공유해서 사용. (다른 프로그램 언어의 정적 상태와 동일하다)
class ObjectClass {
    object ObjectTest {
        const val CONST_STRING = "const string"
        fun test() {
            println("Object 선언: $CONST_STRING")
        }
    }
}

class CompanionClass {
    companion object {
        const val CONST_STRING = "companion const string"
        fun test() {
            println("Companion 선언: $CONST_STRING")
        }
    }
}

fun main() {
    ObjectClass.ObjectTest.test() // Object 선언: const string
    CompanionClass.test() // Companion 선언: companion const string
}

05 확장 알아보기

역시 공식문서가 더 깔끔하고 이해하기 쉬워 공식문서로 대체합니다.

[코틀린 공식문서] Extensions; 확장

 

[코틀린 공식문서] Extensions; 확장

해당 블로깅은 코틀린 공식문서의 Classes and objects의 Extensions를 번역하며 학습한 내용입니다. 학습중임에 따라 추가되고 의역된 부분이 있습니다. 혹시 잘못된 설명이 있다면 얼마든지 제보해주

messycode.tistory.com

 

반응형