티스토리 뷰
728x90
클래스 계층
- 상속
- 주생성자 뒤에 :를 넣고 상위 클래스 이름을 넣는다.
- 상위 클래스를 명시하지 않으면 Any를 상속하는 것으로 간주한다.
- open 키워드는 상속에 열려있다는 의미다.
- 데이터 클래스: oepn x. 다른 클래스 상속 o
- 인라인 클래스: open x. 다른 클래스 상속 x
- 객체: open x. 다른 클래스 상속 o
- 클래스 멤버 vs 확장
open class Parent {
open fun print() { println("parent") }
}
fun Parent.ext() { println("parent extension") }
class Child : Parent() {
override fun print() { println("child") }
}
fun Child.ext() { println("child extension") }
fun main() {
val c: Parent = Child()
c.print() // child
c.ext() // parent
}
-
- 클래스 멤버: 런타임에 인스턴스의 구체적 타입에 따라 어떤 구현이 호출될지 결정된다.
- 확장: 항상 정적으로 호출할 대상의 타입이 결정된다.
- 정적 타입 → Parent, 동적 타입 → Child
open class Parent {
open val i: Int get() = 0
open fun test(): Int? = i
}
class Child(override var i: Int) : Parent() {
override fun test(): Int = i
}
- 상위 클래스의 메서드나 프로퍼티를 오버라이드할 때는 앞에 override를 붙인다.
- 오버라이드하는 함수의 시그니처가 상위 클래스의 시그니처와 같아야 한다.
- 반환 타입을 하위 타입으로 바꿀 수 있다.
- 주생성자 파라미터로도 프로퍼티를 오버라이드할 수 있다.
- 불변 프로퍼티를 가변 프로퍼티로 오버라이드할 수 있다.
- 하위 클래스 생성자
open class Person {
val name: String
val age: Int
constructor(name: String, age: Int) {
this.name = name
this.age = age
}
}
class Student(name: String, age: Int) : Person(name, age)
-
- 상위 클래스 이름 뒤에 괄호는 생성자 호출을 의미한다.
- 자신의 파라미터를 이용해 상위 클래스의 생성자에 전달할 수 있다.
open class Person(val name: String, val age: Int)
class Student : Person {
constructor(name: String, age: Int) : super(name, age)
}
- 하위 클래스에서 부생성자를 사용하는 경우, 생성자 시그니처 바로 뒤에 위임 호출을 위치시킨다.
- 상위 클래스의 이름 뒤에 괄호를 붙이지 않는다. (부생성자에서 위임 호출을 해야하기 때문)
- 하위 클래스에 주생성자가 있는 경우, 무조건 주생성자에서 위임 호출해야 한다.
- 상위 클래스에 여러 생성자가 존재하는 경우, 하위 클래스에서는 주생성자를 아예 정의하지 않고 부생성자를 사용해야 한다.
- 생성자 호출 순서
- 상위 클래스 초기화 → 하위 클래스 초기화
- 상위 클래스에서 현재 인스턴스를 코드에 누출하면, 현재 인스턴스는 초기화되지 않은 상태이므로 올바르지 않은 값이 사용된다. (this leak)
- non-nullable 타입이 null이 될 수 있다.
2. 타입 검사와 캐스팅
fun main() {
val obj = arrayOf("1", 2, "3", 4) // Any
var sum = 0
for(o in obj) {
if (o is Int) { // Int로 스마트 캐스트
sum += o
}
}
println(sum)
}
- is/!is로 타입 검사를 할 수 있다.
- 타입 검사 후 변수가 변경되지 않으면 스마트 캐스트가 동작한다.
- 프로퍼티, 커스텀 접근자가 정의된 변수 x
- 위임을 사용하는 프로퍼티/지역 변수, open 멤버 프로퍼티 x
- 검사 후 값을 변경하는 가변 지역 변수 x
- as 연산자를 통해 값의 타입을 강제로 변환할 수 있다.
- 실제 타입과 변환하려는 타입이 일치하지 않은 경우
- as: 예외
- as?: null
3. 추상 클래스
- 생성자를 가질 수 있다. 인스턴스를 호출할 수 없으므로 하위 클래스의 생성자에서 위임 호출로만 호출될 수 있다.
- 추상 프로퍼티는 명시적 접근자, by, 초기화가 불가능하다.
- 암시적으로 상속에 열려있으므로 open 키워드를 지정하지 않아도 된다.
4. 인터페이스
interface A {
fun print()
}
class B : A {
override fun print() {
println("test")
}
}
fun main() {
val b = B()
b.print()
}
- 생성자가 없기 때문에 하위 클래스/인터페이스에서 상위 인터페이스의 이름 뒤에 괄호를 붙이지 않는다.
- 클래스와 동일하게 :을 사용하여 상속한다.
- 멤버 함수와 프로퍼티를 구현할 수 있다. 하위 클래스/인터페이스에서 override하지 않아도 된다.
- backing field를 사용할 수 없다.
interface Car {
fun move() { println("car") }
}
interface Ship {
fun move()
}
class Amphibia : Car, Ship {
override fun move() { super.move() } // Car의 메서드 호출
}
fun main() {
Amphibia().move()
}
- 자바와 동일하게 여러 인터페이스를 상속받을 수 있다.
- 동일한 시그니처의 멤버를 가지는 인터페이스를 둘 이상 상속할 경우, 한 멤버로 합쳐진 멤버를 상속하는 것과 같다.
- 상위 멤버에서 구현이 존재하더라도, 반드시 하위 클래스에서 구현해야 한다.
interface Car {
fun move() { println("car") }
}
interface Ship {
fun move() { println("ship") }
}
class Amphibia : Car, Ship {
override fun move() {
super<Car>.move()
super<Ship>.move()
}
}
fun main() {
Amphibia().move()
}
- 멤버에 대한 구현이 둘 이상 존재하는 경우, super 호출을 한정된 키워드로 사용해야 한다.
5. sealed class
sealed class Result {
class Success(val value: Any) : Result() { /**/ }
class Error(val message: String) : Result() { /**/ }
}
fun message(result: Result) = when(result) {
is Result.Success -> "Success"
is Result.Error -> "Error"
}
- 이넘 클래스와 유사하게 미리 만들어 놓은 자료형을 묶어서 제공하는 클래스
- 같은 패키지 내에서만 sealed class를 상속할 수 있다.
- 직접 인스턴스를 만들 수 없는 추상 클래스에 속한다. (추상 멤버를 가질 수 있음)
- 생성자의 접근 제한자는 디폴트로 protected이며, protected/private만 가능하다.
- 빠진 자료형 없이 when 식을 작성하면 else 를 생략해도 된다.
6. 위임
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}
class Derived(b: Base) : Base by b
fun main() {
val b = BaseImpl(10)
Derived(b).print()
}
- 상위 인터페이스 이름 뒤에 by 키워드를 붙이고, 위임할 인스턴스를 적는다.
- 위임할 인스턴스에 있는 메서드를 통해 인터페이스의 멤버가 구현된다.
- 컴파일러는 위임된 값을 저장하는 필드를 자동으로 만들어준다.
- 클래스 본문에 정의된 프로퍼티를 클래스 위임에 사용할 수는 없다,
- 인터페이스의 멤버를 구현하는 경우만 위임을 쓸 수 있다,
728x90
'kotlin' 카테고리의 다른 글
[kotlin/코틀린] 제네릭 (0) | 2023.07.19 |
---|---|
[kotlin/코틀린] 컬렉션 (0) | 2023.07.19 |
[kotlin/코틀린] 클래스 종류 (이넘 클래스, 데이터 클래스, 인라인 클래스) (0) | 2023.07.19 |
[kotlin/코틀린] 영역 함수 (0) | 2023.07.19 |
[kotlin/코틀린] 확장 (0) | 2023.07.19 |