인트로
추상 클래스는 대략적인 설계의 명세와 공통의 기능을 구현한 클래스로 즉, 구체적이지 않다.
추상 클래스를 상속하는 하위 클래스는 추상 클래스의 내용을 더 구체화 해야한다.
인터페이스 역시 대략적인 설계 명세를 구현하고 인터페이스를 상속하는 하위 클래스에서 이를 구체화 하는 것은 동일하다!
그러나 인터페이스에서는 프로퍼티의 상태 정보를 저장할 수 없다. → 초기화가 불가능
interface Vehicle {
val name : String
val color : String
val weight : Double
}
interface Animal{
val type : String= "dog"
}
-> 이게 안됨!! 프로퍼티 상태 정보를 저
장할 수 없으니까
추상 클래스(abstract class)
구체화되지 않은 클래스이므로 일반적인 객체를 생성하는 방법으로는 인스턴스화 할 수 없다. -> 상속받는 클래스가 필요하다.
abstract 키워드를 class 앞에 붙여 준다.
클래스 내에서 프로퍼티나 메소드도 abstract로 선언할 수 있다. 이렇게 쓴다면 이를 상속받는 클래스에서 구체화하겠다는 의미이다.
But, 이를 사용하기 위해 해당 클래스가 추상 클래스가 되어야 한다.
abstract class Animal(
val name: String,
val feed : String,
val cute : Boolean){
abstract var maxCute : Double
//초기값을 갖는 일반 프로퍼티(인터페이스에서는 불가능)
var age = 5
abstract fun alchol()
abstract fun check()
fun sleeping() {
println("이름 : $name, 먹이 : $feed")
}
}
Animal 클래스의 경우 추상 클래스로 기본적인 설계만 정의하고 있다. 여기서 abstract 키워드가 붙은 maxCute , alchol()과 check()는 상속받은 하위 클래스에서 반드시 재정의 해줘야한다.
추상클래스를 상속받는 하위 클래스를 정의하지 않고 추상 클래스를 사용할 수 있다. 바로 object 키워드
abstract class Printer {
abstract fun print()
}
val myPrinter = object : Printer() {
override fun print() {
println("print 메소드가 재정의되었습니다")
}
}
//하위 클래스 정의 없이 그대로 사용!!!
fun main() {
myPrinter.print()
}
인터페이스(interface)
interface Pet {
var category : String // 추상 프로퍼티
fun feeding() // 추상 메소드
fun patting() { // 구현부를 포함할 수 있다. 구현부를 포함하면 일반 메소드
println("Keep patting")
}
}
프로퍼티에 기본 값을 가질 수 없다.
class Cat(override var category : String) : Pet { // 주생성자를 이용
override fun feeding() {
println("Feeding 메소드가 구현되었습니다.")
}
}
fun main() {
val obj = Cat("Small")
obj.feeding() // 구현된 메소드
obj.patting()// 일반 메소드
}
예외가 있는데, val로 선언한 프로퍼티는 게터를 통해 필요한 내용을 구현할 수 있다.(???)
interface Pet {
var category : String // 추상 프로퍼티
val message : String // val로 선언하면 게터의 구현이 가능하다.
get() = "I'm cutty"
fun feeding() // 추상 메소드
fun patting() { // 구현부를 포함할 수 있다. 구현부를 포함하면 일반 메소드
println("Keep patting")
}
}
인터페이스 이점
코드의 재사용성이 올라간다.
인터페이스 이용시 클래스 간의 의존성을 제거할 수 있다.
다중 상속도 가능하다.
추상 클래스 인터페이스 차이
솔직히 개념적인 부분에서 어떨 때 추상 클래스를 사용하고 인터페이스를 사용해야 하는지 감이 잡히지 않아서, 이 부분에 대해서는 먼저 이해하고 작성해야겠다.
하지만 구현적인 부분에서는 차이가 있다.
1. backing filed의 유무
backing field는 간단하게 말하면 프로퍼티의 값을 저장하기 위한 필드이다. -> 따로 정리
아래와 같이 set을 통해 프로퍼티 값을 직접 지정할 때, 무한 루프에 빠지지 않도록 하기 위해 field라는 키워드를 통해 Backing field를 사용한다.
var minCount = 0
set(value) {
if(value >= 0) field = value
}
위에 간단하게 언급한 내용을 바탕으로 했을 때,
추상 클래스의 경우 backing field의 사용이 가능하지만 인터페이스의 경우는 불가능 하다!
2. 다중 상속 가능 여부
위의 이미지에 보여진 대로, interface는 다중 구현이 가능하다.
반면에 추상 클래스에 경우 상속받는 클래스에 단 하나 밖에 상속 받지 못한다.
'Language > kotlin' 카테고리의 다른 글
[Kotlin] Coroutine (0) | 2024.07.23 |
---|---|
[Kotlin] 스코프함수(Scope function) (1) | 2024.07.05 |
[kotlin] Data class 란? (0) | 2024.05.29 |
[kotlin] 정렬 정리 (0) | 2023.12.19 |
[kotlin] 배열 (1) | 2023.12.15 |