Skip to content

Latest commit

 

History

History
287 lines (201 loc) · 7.94 KB

5장.md

File metadata and controls

287 lines (201 loc) · 7.94 KB

주 생성자

생성자가 무엇인지는 아실텐데요. 코틀린에서는 생성자를 어떻게 관리하는지 알아보겠습니다.

class Bird (a: String, b: String) {
}

위와 같이 클래스 () 안에 필드를 넣어주면 매개변수 a, b를 가진 생성자가 생성됩니다. 이것을 코틀린에서는 주 생성자라고 부릅니다.



부 생성자

class Bird {
    var name: String
    var wing: Int

    constructor(name: String, wing: Int) {
        this.name= name
        this.wing = wing
    }

}

위와 같이 constructor() {}를 사용해서 부생성자를 사용할 수 있습니다. 부 생성자도 여러 개를 가질 수 있습니다.



초기화 블록을 가진 주 생성자

class Bird (var name: String, var wing: Int, var beak: String, var color: String) {

    // 초기화 블록
    init {
        println("----초기화 블록 시작----")
        println("이름은 $name, 부리는 $beak")
        println("----초기화 블록 끝----")
    }
}

객체를 생성할 때 변수 이외에 초기화할 것이 있다면 위와 같이 초기화 블록을 사용할 수도 있습니다.



프로퍼티의 기본 값 지정

class Bird (var name: String = "NONAME", var wing: Int = 2, var beak: String, var color: String) {
}

위와 같이 기본 값을 지정하면 객체 생성 과정에서 기본 값이 지정된 프로퍼티들은 생략할 수 있습니다.



상속과 다형성

// 상속 가능한 클래스를 선언하게 위해 oepn 사용
open class Bird(var name: String, var wing: Int, var beak: String, var color: String) {

}

// 주 생성자를 사용하는 사용
class Lark(name: String, wing: Int, beak: String, color: String): Bird(name, wing, beak, color) {

}

// 부 생성자를 사용한 상속
class Parrot: Bird {
    val language: String

    constructor(name: String, 
                wing: Int, 
                beak: String, 
                color: String, 
                language: String): super(name, wing, beak, color) {
        this.language = language
    }
}

코틀린에서는 디폴트가 final class 이기 때문에 상속을 하기 위해서는 open 키워드를 사용해야 합니다. 그리고 Parrot 클래스의 코드가 뭔가 알긴 알겠는데 확실하게 감이 오지 않아서 자바 코드로 DeCompile 해보았습니다.


public final class Parrot extends Bird {
   
   private final String language;
   
   public Parrot(String name, int wing, String beak, String color, String language) {
      super(name, wing, beak, color);
      this.language = language;
   }
}

코드를 정리해서 생성자 부분만 가져오면 위와 같습니다. 자바 코드와 비교하면 Kotlin의 코드가 어떤 의미인지 이해할 수 있을 것입니다.



오버 로딩

오버로딩은 동일한 클래스에서 같은 이름의 메소드가 매개변수만 달리해서 여러 번 정의될 수 있는 개념입니다.

fun add(x: Int, y: Int): Int {
    return x + y
}

fun add(x: Double, y: Double): Double {
    return x + y
}

fun add(x: Int, y: Int, z: Int): Int {
    return x + y + z
}

코틀린에서도 자바와 똑같이 오버로딩 기능을 사용할 수 있습니다.



오버라이딩

open class Bird() {

    fun fly() {}          // 최종 메소드로 오버라이딩 불가
    open fun sing() {}    // sing() 메소드는 하위 클래스에서 오버라이딩 가능
}

class Lark(): Bird() {
    // fun fly()  재정의 불가!

    override fun sing() {
        super.sing()
    }
}

class 앞에 open을 명시적으로 적어주어야 상속, 오버라이딩이 가능합니다. 오버라이딩도 자바와 똑같습니다.



super와 this의 참조

// 상속 가능한 클래스를 선언하게 위해 oepn 사용
open class Bird(var name: String, var wing: Int, var beak: String, var color: String) {

    fun fly() {}          // 최종 메소드로 오버라이딩 불가
    open fun sing() = println("Gyunny")
}

class Parrot(name: String, wing: Int = 2, beak: String, color: String,
             var language: String = "natural") : Bird(name, wing, beak, color) {

    override fun sing() {
        super.sing()  // 상위 클래스의 sing()을 먼저 수행
    }
}

super와 this도 자바에 있는 개념인데요. 코틀린에서는 어떻게 사용하는지 알아 보겠습니다. super를 사용해서 부모 클래스의 메소드를 호출할 수 있습니다.



this로 현재 객체 참조하기

open class Person {
    constructor(firstName: String) {
        println("firstName: $firstName")
    }

    constructor(firstName: String, age: Int) {  // 3
        println("age: $age")
    }
}

class Developer: Person {
    constructor(firstName: String): this(firstName, 10) { // 1
        println("firstName: $firstName")
    }

    constructor(firstName: String, age: Int): super(firstName, age) { // 2
        println("Result: $firstName, $age")
    }
}

main() 함수에서 Developer 클래스의 매개변수가 하나인 객체를 생성했다면 어떤 일이 벌어질까요? 당연히 firstName 하나만 가지고 있는 생성자가 호출될 것입니다.

호출된 생성자 앞에 this(firstName, 10)이 존재하기 때문에 Developer 클래스의 매개변수가 2개인 생성자가 호출됩니다. 그런데 이 생성자 앞에도 super(firstName, age)가 있기 때문에 Person 클래스의 매개변수가 2개인 생성자를 호출하게 됩니다. 그래서 위에 적힌 대로 1 -> 2 -> 3 순서대로 호출되게 됩니다.


public final class Developer extends Person {
   
    public Developer(String firstName) {
      this(firstName, 10);
      String var2 = "firstName: " + firstName;
      boolean var3 = false;
      System.out.println(var2);
   }

   public Developer(String firstName, int age) {
      super(firstName, age);
      String var3 = "Result: " + firstName + ", " + age;
      boolean var4 = false;
      System.out.println(var3);
   }
}

예측할 수 있지만 코틀린 코드를 Decompile 해보면 위와 같은 자바 코드로 바꾸어 주는 것을 알 수 있습니다.



주 생성자와 부 생성자 함께 사용하기

class Person(firstName: String, age: Int) {

    constructor(firstName: String, age: Int, lastName: String): this(firstName, age) {}
}

this를 사용해 주 생성자를 가르켜서 사용할 수 있습니다.



바깥 클래스 호출하기

클래스를 선언할 때 클래스 안에 다시 클래스를 선언하는 것이 가능합니다. 이렇게 클래스 안에 선언된 클래스를 Inner Class 라고 합니다.

open class Base {
    open val x: Int = 1
    open fun f() = println("Base Class f()")
}

class Child : Base() {

    override val x: Int = super.x + 1

    override fun f() = println("Chile Class f()")

    inner class Inside {
        fun f() = println("Inside Class f()")
        fun test() {
            f()           // 현재 Inner Class 함수 접근
            Child().f()   // 바로 바깥 클래스 f() 접근
        }
    }
}

fun main() {
    val c1 = Child()
    c1.Inside().test()   // Inner class inside 메소드 test() 실행
}

위의 예제 코드를 보면 Inner Class 접근 방법에 대해서 알 수 있습니다.



가시성 지시자

- private: 이 요소는 외부에서 접근할 수 없다.
- public: 이 요소는 어디서든 접근이 가능하다.
- protected: 외부에서 접근할 수 없으나 하위 상속 요소에서는 가능하다.
- internal: 같은 정의의 모듈 내부에서는 접근이 가능하다.

스크린샷 2021-08-17 오후 4 16 23