코틀린 언어 정리 2-4

class

Any

코틀린에서 모든 class는 Any의 sub class입니다.(더 정확하게는 null이 허용되는 Any?)

Any class에는 equals와 hashCode, toString등의 멤버 함수가 있습니다. run, apply, also, let, with 등의 범위 함수도 Generic Extension function으로 가지고 있습니다.( 이 함수들에 대해서는 코틀린 정리 3. 함수형 프로그래밍에서 확인할 수 있습니다. )



생성자 호출 순서

클래스의 내부( 프로퍼티 초기화, init block )를 주생성자의 구현부로 간주한다면 C/C++ 등의 객체지향 언어와 생성자 호출 순서가 동일합니다. 하위 클래스의 생성자 구현에서 상위 클래스의 생성자를 가장 처음에 호출하고 이 후 하위 클래스의 구현부를 실행하는 순서입니다. 하지만 객체 생성시 주생성자가 존재하는 경우 반드시 호출된다는 점이 다른 언어와 다를 수 있습니다. ( 보조생성자 호출 시 주생성자를 먼저 호출하도록 구현을 강제합니다. )


다음에 나오는 예제를 실행해보면 객체 생성 시 호출되는 코드의 순서를 직접 확인해 볼 수 있습니다.

예제

open class SuperA ( name: String, age: Int ) {

   var homeSuper: String = "dummy".also { println("SuperA property initialize. homeSuper")}

   var age:Int

   init {

       println("SuperA::init")

       this.age = age

   }

   val name:String = name.also { println("SuperA property initialize.")}

   constructor ( name: String ): this ( name, 40 ) {

       println("SuperA::constructor secondary")

       age = 40

   }

}


// 주생성자가 존재하는 경우 상위 클래스의 생성자는 주생성자에서만 접근 가능함

class SubA ( name:String, age: Int, company: String ): SuperA ( name, age.also { println("SuperA( name, age ) from SubA( name, age, company )")} ) {

   var home: String = "dummy".also { println("SubA property initialize. home")}

   init {

       println("SubA::init")

   }

   var company: String = company.also { println("SubA property initialize. company")}


   // 주생성자가 존재할 경우 보조생성자에서는 반드시 주생성자를 호출해 주어야 함

   constructor ( name: String, age: Int ): this ( name, age, "unknown".also { println("SubA( name, age, company ) from constructor ( name, age )") } ) {

       println("SubA::constructor secondary")

   }

}


class SubAA: SuperA var home: String = "dummy".also { println("SubAA property initialize. home")}

   init {

       println("SubAA::init")

       this.company = "unknown"

   }

   var company: String

   var home1: String = "dummy".also { println("SubAA property initialize. home1")}


   // 주생성자가 없는 경우 보조생성자에서 원하는 상위클래스 생성자를 호출할 수 있음

   constructor ( name: String, age: Int, company: String ): super ( name, age.also { println("SuperA ( name, age ) from SubAA::constructor( name, age, company )") } ) {

       println("SubAA::constructor( name, age, company ) secondary")

       this.company = company

   }


   constructor ( name: String ): super ( name.also { println("SuperA ( name, age ) from SubAA::constructor( name, age, company )") } ) {

       println("SubAA::constructor( name ) secondary")

   }

}


fun main ( args: Array<String> ) {

   var objSuper: SuperA = SubA ( "Ree", 40, "unknown".also { println("SubA( name, age, company) from main")} )

   var objSub: SubA = objSuper as SubA

   println("------------------------")

   var objSuper1: SuperA = SubAA ( "Ree".also { println("SubAA( name ) from main") } )

}

이 예제 실행 결과를 기반으로 생성 과정에 실행되는 코드의 순서를 확인할 수 있습니다.


주생성자로 호출한 경우

예제 실행 결과

SubA( name, age, company) from main

SuperA( name, age ) from SubA( name, age, company )

SuperA property initialize. homeSuper

SuperA::init

SuperA property initialize.

SubA property initialize. home

SubA::init

SubA property initialize. company


결과 정리

1. 상위 클래스의 프로퍼티 초기화와 init block이 선언된 순서대로 실행됨

2. 하위 클래스의 프로퍼티 초기화와 init block이 선언된 순서대로 실행됨


보조생성자로 호출한 경우

예제 실행 결과

SubAA( name ) from main

SuperA ( name, age ) from SubAA::constructor( name, age, company )

SuperA property initialize. homeSuper

SuperA::init

SuperA property initialize.

SuperA::constructor secondary

SubAA property initialize. home

SubAA::init

SubAA property initialize. home1

SubAA::constructor( name ) secondary


결과 정리

1. 상위 클래스의 프로퍼티 초기화와 init block이 선언된 순서대로 실행됨

2. 상위 클래스의 보조생성자 실행됨

3. 하위 클래스의 프로퍼티 초기화와 init block이 선언된 순서대로 실행됨

4. 하위 클래스의 보조생성자 실행됨


주생성자와 다르게 보조생성자는 구현부가 있으므로 실행되는 영역이 추가되었다.( 2, 3 과정이 추가됨 ) 이 점을 제외하면 나머지 동작은 주생성자의 경우와 동일합니다.


+ Recent posts