// Kotlin의 클래스는 기본 생성자와 하나 이상의 secondary 생성자를 가질 수 있습니다. // 기본 생성자는 클래스 헤더의 일부분(타입 파라미터 포함)이며 클래스 이름을 따라갑니다. classPersonconstructor(firstName:String){ }
// 기본 생성자에 annotation 또는 visibility modifier가 없는 경우 constructor 키워드를 생략 할 수 있습니다. classPerson(firstName:String){ }
// 기본 생성자에는 코드가 포함될 수 없습니다. // 초기화 코드는 init 키워드로 시작되는 initializer block에 위치 할 수 있습니다. classCustomer(name:String){ init{ logger.info("Customer initialized with value ${name}") } }
// 기본 생성자의 매개 변수는 initializer block에서 사용할 수 있습니다. // 또한 클래스 본문에 선언된 속성의 초기화에도 사용할 수 있습니다. classCustomer(name:String){ valcustomerKey=name.toUpperCase() }
// 사실, 속성을 선언하고 기본 생성자에서 속성을 초기화하기 위해 Kotlin은 다음과 같은 syntax를 제공하고 있습니다. // 일반 프로퍼티와 동일하게 기본 생성자에 선언하는 프로퍼티 역시 mutable(var) 또는 real-only(val) 형태로 선언할 수 있습니다. classPerson(valfirstName:String,vallastName:String,varage:Int){ // ... }
// 생성자에 annotation 또는 visibility modifier가 있는경우 생성자 키워드가 필요하며, 그 앞에 annotation 또는 visibility modifier를 선언합니다. classCustomerpublic@Injectconstructor(name:String){...}
// 클래스에는 constructor 접두어가 붙은 secondary 생성자도 선언 할 수 있습니다. classPerson{ constructor(parent:Person){ parent.children.add(this) } }
// 클래스에 primary 생성자가 있으면 secondary 생성자는 primary 생성자에 직접 또는 간접적으로 위임하여야 합니다. // 동일한 클래스의 다른 생성자에 대한 위임은 this 키워드를 사용하여 수행합니다. classPerson(valname:String){ constructor(name:String,parent:Person):this(name){ parent.children.add(this) } }
// 추상클래스가 아닌경우 생성자를(primary or secondary)를 선언하지 않으면 인수가없는 primary 생성자를 갖습니다. // 생성자의 접근범위는 public입니다. // 클래스에 public 생성자가 없도록하려면 비어있는 private 생성자를 선언해야합니다. classDontCreateMeprivateconstructor(){ }
// NOTE: JVM에서 primary 생성자의 모든 매개 변수에 기본값이 있으면 컴파일러에서 기본으로 사용할 추가 매개 변수없는 생성자를 생성합니다. // 따라서 Kotlin을 매개 변수없는 생성자를 통해 클래스 인스턴스를 만드는 Jackson이나 JPA와 같은 라이브러리와 함께 사용하는 것이 더 쉽습니다. classCustomer(valcustomerName:String="")
클래스의 인스턴스 만들기
1 2 3 4 5
// 인스턴스를 생성하려면 클래스를 일반함수처럼 호출하면 됩니다. // Kotlin은 new 키워드를 사용하지 않습니다. valinvoice=Invoice()
// Kotlin의 모든 클래스는 슈퍼클래스인 Any를 소유합니다. // Any는 java.lang.Object가 아닙니다. // 특히 equals (), hashCode () 및 toString () 이외의 멤버가 없습니다. classExample// 암묵적으로 Any를 상속
// 명시적으로 supertype을 선언하려면 클래스 헤더에 콜론과 함께 유형을 위치시킴니다. // 클래스에 primary 생성자가 있으면 primary 생성자의 매개 변수를 사용하여 supertype을 바로 초기화 할 수 있습니다. // open annotation은 Java의 final과 반대입니다. // Kotlin의 모든 클래스는 final이므로 open annotation이 선언되어야 다른 클래스에서 이 클래스를 상속받을 수 있습니다. openclassBase(p:Int)
classDerived(p:Int):Base(p)
// 클래스에 기본 생성자가 없으면 각 secondary 생성자는 super 키워드를 사용하여 supertype을 초기화하거나 이를 수행하는 다른 생성자에 위임해야합니다. // 이 경우, primary 생성자는 supertype의 다른 생성자를 호출 할 수 있습니다. classMyView:View{ constructor(ctx:Context):super(ctx)
// 그리고 Java와는 달리 Kotlin은 오버라이딩 할 수 있는 멤버에 대한 명시적인 annotation(코틀린에서는 이것이 open이라는 키워드로 불림)을 요구합니다. // 오버라이딩 구현시 override annotation을 사용해야 하며 그렇지 않으면 컴파일을 할 수 없습니다. // Base.nv ()와 같은 함수에 open annotation이 없으면 서브 클래스에서 동일한 signature를 가진 메소드에 대한 선언이 금지됩니다. openclassBase{ openfunv(){} funnv(){} } classDerived():Base(){ overridefunv(){} }
// override가 선언된 멤버는 자체적으로 열려 있습니다. // 즉, 하위 클래스에서 재정의 될 수 있습니다. // 하위클래스에서의 오버라이딩을 금지하려면 final을 사용하면 됩니다. openclassAnotherDerived():Base(){ finaloverridefunv(){} }
// 프로퍼티 오버라이딩은 메서드 오버라이딩과 비슷한 방식으로 작동합니다. // 파생클래스에서 선언되는 프로퍼티는 override로 시작되어야 하며, 호환가능한 타입이어야 합니다. // 선언된 각 프로퍼티는 initializer 또는 getter method와 함께 재정의 될 수 있습니다. openclassFoo{ openvalx:Intget(){...} }
classBar1:Foo(){ overridevalx:Int=... }
// var프로퍼티는 val프로퍼티로 재정의 할 수도 있지만 그 반대는 불가합니다. // 이는 val프로퍼티가 기본적으로 getter method를 선언하고 var로 재정의되면 파생클래스에서 setter method를 추가로 선언하기 때문에 허용됩니다. // 기본 생성자에서 속성 선언의 일부로 override 키워드를 사용할 수 있습니다. interfaceFoo{ valcount:Int }
// innerclass 내부에서 외부 클래스의 superclass 액세스하는 것은 외부 클래스 이름으로 수식 된 super 키워드(super@Outer)를 이용하면 됩니다. classBar:Foo(){ overridefunf(){/* ... */} overridevalx:Intget()=0
innerclassBaz{ fung(){ super@Bar.f()// Calls Foo's implementation of f() println(super@Bar.x)// Uses Foo's implementation of x's getter } } }
오버라이딩 규칙
Kotlin에서 상속은 다음 규칙에 의해 규제됩니다.
클래스가 직접 superclass에서 동일한 멤버의 많은 구현을 상속하는 경우 해당 멤버를 재정의하고 자체 구현을 제공해야합니다(상속 된 클래스 중 하나를 사용하는 경우).
supertype을 나타내기 위해서는 꺽쇠괄호(super) 안에 supertype을 선언하면됩니다.
// A와 B 모두에서 상속하는 것이 좋습니다. // a(), b()의 경우 상속받은 클래스 안에 하나의 함수만 존재하므로 문제가 되지 않습니다. // 그러나 f ()의 경우 C에 의해 상속 된 동일한 이름의 함수가 존재하므로 C에서 f()를 재정의하고 모호성을 제거해야 합니다. openclassA{ openfunf(){print("A")} funa(){print("a")} }
interfaceB{ funf(){print("B")}// interface members are 'open' by default funb(){print("b")} }
classC():A(),B{ // The compiler requires f() to be overridden: overridefunf(){ super<A>.f()// call to A.f() super<B>.f()// call to B.f() } }
추상클래스
클래스와 멤버 중 일부는 abstract로 선언 될 수 있습니다.
abstract 멤버는 클래스 내에 구현되지 않습니다.
abstract가 아닌 멤버를 abstract 멤버로 재정의 할수도 있습니다.
Kotlin에서는 Java 또는 C#과 달리 클래스에 정적 메소드가 없습니다.
대부분의 경우 단순히 패키지수준의 함수를 사용하는것이 좋습니다.
클래스 인스턴스가 없어도 호출 할 수 있지만 클래스의 내부(예: 팩토리 메소드)에 액세스해야하는 함수를 작성해야하는 경우 해당 클래스 내에 오브젝트 선언의 멤버로 작성할 수 있습니다.
보다 구체적으로, 클래스 내에 Companion Object를 선언하면 클래스 이름만 한정자로 사용하여 Java/C#에서 정적 메소드를 호출하는 것과 동일한 구문으로 멤버를 호출 할 수 있습니다.