클래스와 상속
클래스
- class 키워드를 사용하여 선언
- 헤더와 본문은 옵션. 본문이 없는 경우 중괄호 생략 가능
class Invoice {
}
생성자
- 기본 생성자와 하나이상의 보조 생성자를 가질 수 있다
- constructor 키워드 생략 가능
- 기본값을 가질 수 있음
class Person(val firstName: String = "") {
}
// class Person constructor(firstName: String) {
// }
- 초기하는 init 키워드를 이용
class Customer(name: String) {
init {
logger.info("Customer initialized with value ${name}")
}
}
- 기본 생성자의 파라매터를 프로퍼티 초기화에 사용 가능
class Customer(name: String) {
val customerKey = name.toUpperCase()
}
- 어노테이션 / 가시성 제한자를 가지는 경우 constructor 키워드가 필요
class Customer public @Inject constructor(name: String) { ... }
보조 생성자
- constructor 키워드를 이용하여 보조 생성자 선언 가능
- 생성자 오버라이딩은 this 키워드를 이용
class Person(val name: String) {
constructor(name: String, parent: Person) : this(name) {
parent.children.add(this)
}
}
- 공개 생성자를 원하지 않으면 private constructor 사용
class DontCreateMe private constructor () {
}
클래스 인스턴스 작성
- new 키워드가 존재하지않음
val invoice = Invoice()
val customer = Customer("Joe Smith")
상속
- 모든 클래스에는 공통의
Any
슈퍼 클래스가 존재- Any != java.lang.Object
- equals(), hasCode(), toString()
class Example // Implicitly inherits from Any
자바 상호 운용 참고
- 상속 선언
open class Base(p: Int)
class Derived(p: Int) : Base(p)
- 주요 기본 생성자가 없는 경우는 super 키워드를 사용해서 기본타입 초기화 혹은 다른 생성자에게 위임
class MyView : View {
constructor(ctx: Context) : super(ctx)
constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
}
- open 어노테이션은 자바의 final과 반대
- 기본적인 코틀린의 클래스는 final
- Effective Java 의 Item 17 : Design and document for inheritance or else prohibit it 내용에 의거
메소드 오버라이딩
- 오버라이드 멤버를 명시적으로할 필요
- open : override 가능한 멤버 지정
- override : override 한 멤버 지정
- final 클래스에서는 open 멤버가 금지
- override를 명시한 멤버는 open이다. 오버라이딩을 막고 싶으면 final 을 사용
open class Base {
open fun v() {}
fun nv() {}
}
class Derived() : Base() {
override fun v() {}
}
open class AnotherDerived() : Base() {
final override fun v() {}
}
Derived.v()
에 override 어노테이션을 붙이지 않으면 컴파일 에러 발생
프로퍼티 오버라이딩
- 메소드 오버라이드와 동일한 매커니즘
val
프로퍼티를var
프로퍼티로 오버라이딩 가능. 그 반대는 불가능
오버라이딩 규칙
- 동일한 함수를 override 하는 경우
super<Base>
으로 호출 가능
open class A {
open fun f() { print("A") }
fun a() { print("a") }
}
interface B {
fun f() { print("B") } // interface는 기본으로 open
fun b() { print("b") }
}
class C() : A(), B {
// 컴파일을 위해 f()를 오버라이딩
override fun f() {
super<A>.f() // call to A.f()
super<B>.f() // call to B.f()
}
}
추상 클래스
- 클래스와 메소드를 abstract로 선언
- 추상 메소드는 구현을 가지지 않는다
- open을 붙일 필요 없다
- 비상적인 공개 메소드를 추상 메소드로 override 가능
open class Base {
open fun f() {}
}
abstract class Derived : Base() {
override abstract fun f()
}
Companion Objects
- Kotlin에서는 정적 메소드는 없음 → 패키지 레벨의 함수 사용을 권장
- 클래스 내부에 접근할 필요가 있는 함수를 작성할 경우 (예. 팩토리 메소드)에는 클래스 내의 선언한 오브젝트의 멤버로 기술가능
- 클래스 내에 Companion Object를 선언함으로 클래스명으로 Java/C#의 정적 메소드 호출과 같은 구문으로 멤버 호출이 가능