1. 추상클래스
추상클래스의 이해를 돕기 위해 먼저 동물이라는 상위클래스와 고양이, 펭귄 이라는 하위클래스를 만들어보자!
위의 자바 클래스를 코틀린으로 바꾸어보자!
abstract class Animal(
protected val species : String,
protected open val legCount : Int
//프로퍼티를 오버라이드 할 때 추상 프로퍼티가 아니라면
//무조건 open을 붙여줘야만함
) {
abstract fun move()
}
이렇게 변경시킬 수 있고, 위의 open이라는 지시어는 오버라이드를 할 수 있게 열어준다는 뜻이다!
먼저 Cat클래스를 만들어 Animal클래스를 상속받아보자.
class Cat(
species : String
) : Animal(species, 4) {
/*
extends를 사용하지 않고 : 를 사용함
어떤 클래스를 상속받을 때 상위 클래스의 생성자를 바로 호출해야함
오버라이드를 필수적으로 붙여줘야함
*/
override fun move() {
println("고양이가 사뿐 사뿐 걸어가~")
}
}
자바에서는 extends를 사용했지만 코틀린에서는 : 를 사용하여 상속받는다!
또한, 어떤 클래스를 상속받을 때 위와 같이 상위 클래스의 생성자를 바로 호출해주어야 한다!!!!
마지막으로 오버라이드를 할 때에는 자바에서는 override라는 어노테이션을 사용하였지만
코틀린에서는 어노테이션을 사용하지 않고, 지시어를 사용하여 오버라이드한다!
다음, Animal클래스를 상속받은 Penguin클래스를 살펴보자!
위의 자바 코드에서는 Cat클래스와 다르게 펭귄클래스에서만 사용하면 wingCount라는 변수가 생겼고,
이는 LegCount에 덧셈해서 Get을 해준다는 차이가 있다.
이를 이제 코틀린으로 바꾸어보자!
class Penguin(
species : String
) : Animal(species, 2), Swimable, Flyable {
//인터페이스 구현도 똑같이 : 을 사용
private val wingCount : Int = 2
override fun move() {
println("펭귄이 움직인다~ 꿱꿱")
}
//custom getter
override val legCount : Int
get() = super.legCount + this.wingCount
Cat과 같이 move메소드를 오버라이드를 먼저 진행하였고,
legCount라는 프로퍼티를 get하는 메소드를 오버라이드 하는것이니까 위처럼 custom getter를 사용하여 오버라이드 하였다.
2. 인터페이스
interface Swimable {
val swimAbility : Int
get() = 3
fun act(){
println("어푸 어푸")
}
}
interface Flyable {
fun act(){
println("파닥 파닥")
}
}
펭귄 클래스에서 위를 인터페이스를 구현해보자!
class Penguin(
species : String
) : Animal(species, 2), Swimable, Flyable {
인터페이스도 상속과 같이 : 를 통해 구현한다.
override fun act() {
super<Swimable>.act()
super<Flyable>.act()
}
하지만 위에서 차이점이 존재한다.
중복되는 인터페이스를 특정할 때 super<타입>.함수이름 을 사용하여 오버라이드한다!!!
또한, Swimable에서 swimAbility라는 변수가 있는데, 이는 미리 정의를 안해주면 아래에서 꼭 오버라이드해서 사용해주어야한다!
//커스텀 게터를 통해
override val swimAbility: Int
get() = TODO("Not yet implemented")
이렇게 커스텀 getter를 통해 반드시 오버라이드 해주어야 한다!
하지만 미리 정의가 되어있으면 꼭 필요한 것은 아니다.
이렇게 인터페이스에서 backing field없는 프로퍼티를 만들 수 있다는 특징도 참고해두면 좋다!!
3. 클래스를 상속할 때 주의할 점
fun main(){
Dervied(300)
}
open class Base(
open val number : Int = 100
){
init{
println("Base Class")
println(number)
}
}
class Dervied(
override val number : Int
) : Base(number){
init{
println(number)
println("Dervied Class")
}
}
이렇게 하면 println(number)에서 300이 나올 것 같지만 0이라는 결과가 출력된다.
먼저 Base Class가 출력이 되는데, 이 말은 상위클래스에 init이 먼저 실행이 된다라고 해석이 된다.
상위클래스 생성자가 실행하는 동안 하위클래스의 프로퍼티 인스턴스화 한다는 것은
Dervied에 있는 number에 값을 집어 넣어준다는 건데,
이 때 상위 클래스에서 넘버를 호출하게 되면
하위클래스에 있는 넘버를 가져오게 된다.
근데 아직 상위클래스에 있는 생성자가 먼저 실행된 단계라서
하위클래스에 number라는 값에 초기화가 이루어지지 않았던 것이다.
그 상태에서 하위클래스에 있는 number에 접근하게 되니까
100도 300도 아닌 int의 초기값 0이 출력이 되는 것이다!!
그래서 애초에 상위클래스에서는 하위클래스의 field에 접근하면 안 된다고 이야기하고 있다.
따라서 상위클래스를 설계할 때 생성자 또는 초기화 블록에 사용되는 프로퍼티에는 open을 피해야한다!!!!!
**
final : override를 할 수 없게 한다. default로 보이지 않게 존재한다.
open : override를 열어준다.
abstract : 반드시 override 해야한다
override : 상위 타입을 오버라이드 하고 있다.
코틀린에서는 어노테이션이 아니라 키워드로 사용해야한다.
'Kotlin > Basic' 카테고리의 다른 글
[Kotlin/Basic] 코틀린에서의 object 키워드 (0) | 2022.08.31 |
---|---|
[Kotlin/Basic] 코틀린에서의 접근제어 (0) | 2022.08.23 |
[Kotlin/Basic] 코틀린에서의 클래스 (0) | 2022.08.15 |
[Kotlin/Basic] 코틀린에서의 함수 (0) | 2022.08.14 |
[Kotlin/Basic] 코틀린에서의 예외처리 (0) | 2022.08.13 |