ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [코틀린] 객체와 클래스
    코틀린 2022. 3. 13. 00:49

    1. 객체와 싱글톤

    객체 표현식으로 사용하는 익명 객체

    객체 표현식: object {} 형태로 구현하고 익명의 객체를 생성한다.

    fun drawCircle() {
        val circle = object {
            val x = 10
            val y = 20
            val radius = 5
        }
        println("Circle x: ${circle.x} y: ${circle.y} radius: ${circle.radius}"))
    }
    
    drawCircle() // Circle x: 10 y: 20 radius: 5

    익명 객체는 인터페이스를 구현할 수도 있다. object: <인터페이스명> 으로 인터페이스를 구현하는 익명 객체를 만들 수 있다.

    fun createRunnable(): Runnable {
        val runnable = object: Runnable {
            override fun run() {
                println("You called...")
            }
        }
        return runnable
    }
    
    createRunnable().run() //You called...

    익명 객체가 Single abstract method interface(인터페이스 내에 추상 메소드가 1개일 경우) 이면 아래처럼 간단하게 구현할 수 있다. 오버라이드 하는 메소드의 이름을 따로 명시해주지 않아도 된다.

    fun createRunnableSingle(): Runnable = Runnable { println("You called single method") }
    createRunnableSingle().run()

     

    객체 선언을 이용한 싱글톤

    object 와 {} 사이에 이름을 명시하면 익명 객체 생성을 위한 표현식이 아니라 싱글톤 객체를 선언하는 것이다.

    object Util {
        fun numberOfProcessors() = Runtime.getRuntime().availableProcessors()
    }
    
    println(Util.numberOfProcessors()) // 프로세서 수 출력됨

    싱글톤 객체 안에는 val, var를 사용할 수 있다.

    싱글톤 객체가 상속받은 클래스나 인터페이스를 파라미터로 갖는 메소드에 인자로 넘길 수도 있다.

    object Sun: Runnable {
        val radius = 696000
        var temperature = 15000000
        override fun run() {
            println("I`m Sun.")
        }
    }
    
    fun runSun(sun: Runnable) {
        sun.run()
    }
    
    runSun(Sun) // I`m Sun. 출력됨

     

    2. 클래스 생성

    클래스

    코틀린 클래스는 아래처럼 간단하게 정의할 수 있다.

    class Fruit // 가장 간단한 클래스
    
    class Computer(val numberOfProcessor: Int, var color: String)

    Computer 클래스의 괄호 안에는 val 로 선언된 Int형의 numberOfProcessor와 var로 선언된 String형의 color가 정의되어 있다. 코틀린은 자동적으로 생성자, 필드, getter를 정의해준다. val 로 선언된 필드는 객체 생성후 변경할 수 없다. var로 선언되 필드는 객체 생성 후에도 변경할 수 있다. 

     

    인스턴스 생성

    코틀린에서 인스턴스를 만들때는 그냥 함수를 호출하듯이 하면 된다. new 키워드가 없다.

    val computer = Computer(10, "Red")
    println("${computer.numberOfProcessor} ${computer.color}") // 10 Red

    아래는 코틀린 컴파일러가 Computer.kt를 Computer.class로 컴파일한 코드의 일부다.

    Compiled from "Computer.kt"
    public final class chapter07.Computer {
        private final int numberOfProcessor;
        private java.lang.String color;
        public chapter07.Computer(int, java.lang.String);
        public final int getNumberOfProcessor();
        public final java.lang.String getColor();
        public final void setColor(java.lang.String);
    }
    

    변수들을 private으로 선언하고 val변수에 대해서는 getter를, var에 대해서는 getter와 setter를 정의해준다. 코틀린에서 computer.numberOfProcessor를 호출하면 실제로는 computer.getNumberOfProcessor를 호출한 것이다. 

     

    생성자에 제약을 두고 싶다면 아래와 같이 set으로 추가할 수 있다.

    class ComputerWithSetter(val numberOfProcessor: Int, theColor: String) {
        var battery = 100
        set(value) {
            println("set battery")
            field = if(value < 0) 0 else value
        }
        var color = theColor
        set(value: String) {
            println("set color ${value} ${field}")
            if(value.isEmpty()) {
                throw RuntimeException("value is Empty")
            }
            field = value
        }
    }
    
    
    computerWithSetter.color = "Blue"  // set color Blue Black 출력
    computerWithSetter.battery = 0 // set battery 출력

     

    접근 제어자

    코틀린에는 public, private, protected, internal 네 개의 접근 제어자가 있다. 

    • public: 기본 접근 제어자, 어디에서나 멤버를 조회할 수 있음
    • private: 객체 내부에서만 접근 할 수 있음
    • protected: 자식 클래스에서 접근하는 것을 허용함
    • internal: 같은 모듈 내에서만 조회할 수 있음
    class Computer(val numberOfProcessor: Int, var color: String) {
        public val publicA = 100
        internal val internalA = 100
        private val privateA = 100
        protected val protectedA = 100
    }

     

    초기화 코드

    기본적으로 코틀린 클래스는 클래스 파라미터를 파라미터로 하는 주 생성자를 갖는다. 여기에 init 블록을 정의해서 클래스를 초기화하는데 필요한 추가적인 작업을 할 수 있다.

    class Person(_name: String, _age: Int) {
        var name = if(_name.isEmpty()) "Anonymous" else _name
        val age = _age
        init {
            if (_age < 10) {
                name = "junior: $name"
            }
        }
    }

    init블록의 특징은 아래와 같다.

    • 위에서부터 아래로 실행되며 속성과 파라미터를 사용할 수 있다.
    • 여러개를 정의할 수 있다. 꼭 필요한 경우에만 사용하는 것이 좋다.

     

    보조 생성자

    모든 클래스는 아규먼트를 받지 않는 기본 생성자를 가지고 있다. 클래스 파라미터를 가지고 있으면 주 생성자도 함께 생성한다. 

    class Person(val name: String, val age: Int) {
        var location = ""
        var phone = ""
    
        constructor(name: String, age: Int, _phone: String): this(name, age) {
            phone = _phone
        }
        constructor(name: String, age: Int, _location: String, _phone: String): this(name, age, _phone) {
            location = _location
        }
    }

    위와같이 기본으로 생성되는 생성자 외에 추가적으로 생성자를 만들 수도 있다. 이렇게 만든 보조 생성자는 다른 보조생성자나 주 생성자를 호출할 수도 있다.

     

    3. 컴패니언 객체와 클래스 멤버

    컴패니언 객체는 클래스 안에 정의한 싱글톤으로 클래스 레벨의 멤버들을 정의해둘 수 있다.

    class ClassWithCompanion {
        companion object {
            var number: Int = 0
    
            fun companionMethod() {
                println("Companion Method")
            }
        }
        fun printNumber() {
            println(number)
        }
    }
    val c1 = ClassWithCompanion()
    c1.printNumber() // 0 출력
    ClassWithCompanion.number = 100
    val c2 = ClassWithCompanion()
    c2.printNumber() // 100 출력
    ClassWithCompanion.companionMethod() // Companion Method 출력

    ClassWithCompanion 내에 정의된 companion 객체는 number와 companionMethod를 갖는다. 이 속성들은 ClassWithCompanion의 클래스 레벨에서 접근하여 사용할 수 있다.

     

    컴패니언에 접근

    컴패니언 객체를 직접 참조하고 싶으면 Companion 속성을 이용할 수도 있다.

    val companionRef = ClassWithCompanion.Companion
    companionRef.companionMethod() // Companion Method 출력됨

    컴패니언 객체에 이름을 지정해 줄 수도 있다.

    class CompanionWithName {
        companion object AnonymousCompanion {
            fun getName(): String {
                return "Anonymous"
            }
        }
    }
    
    println(CompanionWithName.AnonymousCompanion.getName()) // Anonymous 출력

     

    팩토리로 사용하는 컴패니언

    컴패니언을 팩토리처럼 사용하면 클래스 생성 자체를 컴패니언에 맡길 수도 있다.

    class Person private constructor(val name: String, val nation: String) {
        companion object PersonFactory {
            private const val KOREA = "KOR"
    
            fun createKorean(name: String): Person {
                if(name.isEmpty()) throw IllegalArgumentException("Give name")
                val p = Person(name, KOREA)
                return p
            }
        }
    
    }
    
    // Person("Kim", "KOR") 접근 불가능
    val p = Person.createKorean("Park")
    println("${p.name} ${p.nation}") // Park KOR

    주 생성자를 private constructor를 이용해 private로 만들고 companion object에 객체 생성을 위임했다.

     

    4. 데이터 클래스

    data class는 데이터를 관리하는데 사용하는 클래스다.

    • 주 생성자에는 적어도 하나의 val이나 var가 필요하다. val, var가 없는 파라미터는 사용할 수 없다.
    •  equals, hashCode, toString, copy 메소드를 자동으로 만들어준다.
    • componentN 메소드를 제공해준다
    data class FootballPlayer(val name: String, val height: Int, val injured: Boolean)
    
    val kim = FootballPlayer("Kim", 180, false)
    println(kim) // FootballPlayer(name=Kim, height=180, injured=false)
    val injuredKim = kim.copy(injured=true)
    println(injuredKim) // FootballPlayer(name=Kim, height=180, injured=true)
    println(kim.component1()) // Kim

    copy는 객체의 속성들을 shallow copy해서 새로운 객체를 만드는 메소드이다.

    componentN메소드는 N 번째 속성을 가져오는데 구조분해에 활용된다.

    val (nameOfKim, heightOfKim, _) = kim // kim 객체가 구조분해되어 각 변수에 할당됨
    println("$nameOfKim $heightOfKim") // Kim 180 출력됨

     

    '코틀린' 카테고리의 다른 글

    [코틀린] 델리게이션  (0) 2022.03.16
    [코틀린] 클래스와 상속  (0) 2022.03.13
    [코틀린] 타입 안정성  (0) 2022.03.07
    [코틀린] 컬렉션  (0) 2022.03.07
    [코틀린] 외부 반복과 아규먼트 매칭  (0) 2022.03.07

    댓글

Designed by Tistory.