다양한 클래스와 인터페이스 (1/2)

장똑대·2022년 4월 12일
0

Do it! 코틀린 프로그래밍 [둘째마당, 객체 지향 프로그래밍] 학습

✏️1. 추상 클래스와 인터페이스✏️

1-1. 추상클래스(Abstract Class)

  • 선언 등의 대략적인 설계 명세와 공통의 기능을 구현한 클래스
  • abstract 라는 키워드와 함께 선언
  • 일반적인 객체를 생성하는 방법으로 인스턴스화될 수 없음

📌 추상클래스의 정의와 구현

  • abstract class Vehicle
  • 멤버인 프로퍼티나 메서드도 abstract로 선언될 수 있음
    -> 추상 프로퍼티, 추상 메서드 라고 부름
  • 클래스에 추상 프로퍼티나 메서드가 하나라도 있다면 해당 클래스는 추상클래스가 되어야함
// 추상 클래스 -> 주 생성자에는 비추상 프로퍼티 매개변수가 3개 있음
abstract class Vehicle(val name: String, val color: String, val weight: Double) {
	// 추상 프로퍼티 -> 반드시 하위(자식) 클래스에서 재정의 초기화 필요
	abstract var maxSpeed: Double
    
    // 일반 프로퍼티 -> 초깃값인 상태를 저장 가능
    var year = "2018"
    
    // 추상 메서드 -> 반드시 하위(자식) 클래스에서 구현 필요
    abstract fun start()
    abstract fun stop()
    
    // 일반 메서드 -> 재정의할 필요 X
    fun displaySpecs() {
    	println("$name, $color, $weight")
    }
}

-> Vehicle 클래스는 객체를 생성할 수 없음 (기본 설계 역할만)
-> 추상 클래스에서는 abstract 키워드 자체가 상속과 오버라이딩을 허용하고 있기 때문에 open키워드가 필요없음
-> 일반 프로퍼티나 메서드를 오버라이딩 하기 위해선 open 키워드 필요

⬇️ 위에서 작성한 인터페이스 구현

// maxSpeed는 오버라이딩
class Car(name: String, color: String, weight: Double, override var maxSpeed: Double) : Vehicle(name, color, weight {

	// 추상 메서드 start()와 stop()을 오버라이딩
	override fun start() { /* 재정의 */ }
    override fun stop() { "Car Stop" }
}

class MotorCycle(name: String, color: String, weight: Double, override var maxSpeed: Double) : Vehicle(name, color, weight) {
	override fun start() {
    // 추상 메서드 start()와 stop()을 오버라이딩
	override fun start() { /* 재정의 */ }
    override fun stop() { "MotorCycle Stop" }
}

fun main() {
	val car Car("SuperMatiz", "yellow", 1110.0, 270.0)
    val motor = MotorCycle("DreamBike", "red", 173.0, 100.0)
    
    car.displaySpecs()
    car.stop()
    motor.displaySpecs()
    motor.stop()
}

-> 각 개체의 start()와 stop()은 추상 메서드로부터 오버라이딩되어 하위 클래스에서 구현

📌 추상 클래스로부터 단일 인스턴스로 객체 생성하기

  • 하위(자식)클래스를 생성하지 않고 단일 인스턴스로 객체를 생성하려면 object 키워드를 사용해서 지정할 수 있음
// 추상 클래스
abstract class Printer {
	abstract fun print() // 추상 메서드
}

val myPrinter = object: Printer() { // 객체 인스턴스
	override fun print() { /* 추상 메서드 구현 */ } 
}

fun main() {
	myPrint.print()
}

1-2. 인터페이스(Interface)

  • 인터페이스에는 abstract로 정의된 추상 메서드나 일반 메서드가 포함
    -> 메서드에 구현 내용이 포함 될 수 있음
  • 프로퍼티를 통해 상태를 저장할 수 없음. 선언만 가능
  • 객체를 생성할 수 없고 하위 클래스를 통해 구현 및 생성

📌 인터페이스를 쓰는 이유

  • 상속을 하나만 허용하는 하위 클래스의 단점을 보완
  • 상위 클래스와 하위 클래스의 연관(영향)이 감소
    -> 인터페이스는 클래스가 아님. 따라서 상속이라는 형태로 하위 클래스에 프로퍼티와 메서드를 전하지 않음. 그래서 하위 클래스보다는 구현 클래스라고 함
  • 인터페이스는 원하는 만큼 구현클래스에 붙여서 필요한 메서드를 구현 가능

📌 인터페이스의 선언과 구현

  • 메서드는 추상/일반 메서드 모두 선언이 가능
  • 프로퍼티는 오직 추상 프로퍼티로만 선언

⬇️ 인터페이스의 구조

interface 인터페이스 이름 [: 인터페이스 이름...] {
	추상 프로퍼티 선언
    추상 메서드 선언
    
    [일반 메서드 선언 {...}]
}

⬇️ 인터페이스 선언

interface Pet {
	// abstract키워드가 없어도 기본은 추상 프로퍼티/메서드
	var category: String
    fun feeding()
    
    // 일반 메서드 : 구현부를 포함하면 일반적인 메서드로 기본이 됨
    fun patting() { } // 구현부
}

-> 프로퍼티는 상태를 저장할 수 없기에 기본값을 가질 수 없음

⬇️ 인터페이스로부터 구현 클래스 정의

class Cat(override var category: String) : Pet {
	override fun feeding() { /* 구현부 */ }
}

fun main() {
	val obj = Cat("small")
    obj.feeding() // 구현(오버라이딩)된 메서드
    obj.patting() // 기본 메서드
}

-> Cat 클래스는 Pet인터페이스를 구현한 클래스

📌 게터를 구현한 프로퍼티

  • val로 선언된 프로퍼티는 게터를 통해 필요한 내용을 구현 가능
  • 보조 필드인 field를 사용할 수 없음
interface Pet{
	val msgTags: String
    	get() = "I'm your lovely pet!"
}
...
println("$obj.msgTags")
...

📌 인터페이스 구현의 필요성


1-3. 다중 인터페이스

  • 오로지 1개의 클래스만 상속 가능한 클래스와 달리 인터페이스를 사용하면 여러 인터페이스로부터 구현할 수 있음
interface Bird {
	val wing: Int
    fun jump() { println("Bird Jump") }
}

interface Horse {
	var maxSpeed: Int
    fun jump() { println("Horse Jump") }
}

class pegasus: Bird, Horse { // 인터페이스 다중 상속
	override val wings = 2
    override val maxSpeed = 100
    
    override fun jump() { // 오버라이딩 하지 않아도 상관X
    	super<Horse>.jump() // Horse인터페이스의 jump() 실행
        println("Pegasus Jump!")
    }
}

fun main() {
	val pegasus = Pegasus()
    pegasus.jump
    // Horse Jump 출력 후 Pegasus Jump! 출력
}

-> 인터페이스와 구현 클래스에서 함수명이 동일한 경우 super<인터페이스 이름>.메서드 이름() 형태로 구분할 수 있음

profile
장똑대와 안드로이드

0개의 댓글

관련 채용 정보