+ 항공대학교 김철기 교수님의 객체 지향 프로그래밍 과목 내용를 정리한 글입니다.
인터페이스
일종의 추상 클래스로, 추상 클래스보다 제약이 많으나, 여러 개를 상속 받도록 할 수 있는 특수한 개념이다.
- 원래 클래스는 하나의 상위 클래스만 상속 받을 수 있다.
- 하지만, 인터페이스는 갯수에 제한 없이 상속이 가능하다.
- class 대신에 interface라는 예약어로 시작한다.
- interface의 멤버는 default가 추상(abstract) 멤버이다.
=> 즉, 인터페이스는 자격의 의미이다. (상속 받는 클래스에게 자격을 부여한다.)
interface Vehicle {
// 아래 프로퍼티는 abstract가 붙은 것으로 간주한다.
val currentSpeed : Int
fun move()
fun stop()
}
인터페이스의 상속
인터페이스는 다른 인터페이스만 상속할 수 있다. (class 상속 불가)
비추상 클래스가 interface를 상속하면 interface의 모든 추상 멤버를 구현해야 한다.
인터페이스는 생성자가 없다.
=> 상속 시 ()를 쓰지 않아도 된다.
+ alt + enter를 통해 상속한 인터페이스의 멤버를 구현할 코드를 쉽게 불러올 수 있다.
+ 인터페이스 타입의 변수를 생성할 수 있다.
interface Vehicle {
// 아래 프로퍼티는 abstract가 붙은 것으로 간주한다.
val currentSpeed: Double
fun start()
fun stop()
}
class Car : Vehicle {
override var currentSpeed = 0.0
private set
override fun start() {
println("Start riding")
currentSpeed = 50.0
}
override fun stop() {
println("Stop riding")
currentSpeed = 0.0
}
}
fun main() {
// 인터페이스 타입의 변수를 생성
val v: Vehicle = Car()
v.start()
println("Current Speed = ${v.currentSpeed}")
v.stop()
println("Current Speed = ${v.currentSpeed}")
}
인터페이스 멤버의 구현
인터페이스의 함수나 프로퍼티는 구현을 추가할 수 있다.
인터페이스의 멤버는 구현을 했더라도 default로 open이다.
interface Vehicle {
val currentSpeed: Double
// currentSpeed가 0이면 true, 아니면 false 값을 갖는 Boolean형 변수
// 자신의 상태가 아닌 currentSpeed의 상태를 갖는다.
val isMoving get() = currentSpeed != 0.0
fun start()
fun stop()
fun report() {
println(if(isMoving) "Moving at $currentSpeed" else "Still")
}
}
class Car : Vehicle {
override var currentSpeed = 0.0
private set
override fun start() {
println("Start riding")
currentSpeed = 50.0
}
override fun stop() {
println("Stop riding")
currentSpeed = 0.0
}
}
fun main() {
val v: Vehicle = Car()
v.start()
v.report()
v.stop()
v.report()
}
상태 정의가 금지된 인터페이스
인터페이스는 상태를 가질 수 없다.
- 상태를 가질 수 없으므로 생성자를 가질 수 없다.
따라서 프로퍼티를 구현할 때도 뒷받침하는 필드를 두거나 초기화가 금지된다. (메모리를 가질 수 없다.)
interface Vehicle {
val currentSpeed: Double = 0.0 // Error: 자신의 상태 정의
val maxSpped: Double by lazy { 100.0 } // Error: by lazy 역시 메모리 사용
val altitude: Double // Error
get() { // get()은 사용 가능
println("Altitude get read")
return field // 뒷받침하는 필드 사용
}
}
}
인터페이스와 다중 상속
package kr.kau.main
interface Car {
fun ride()
}
interface Aircraft {
fun fly()
}
interface Ship {
fun sail()
}
// Car interface와 Aircraft interface를 상속하는 FlyingCar interface (다중 상속)
interface FlyingCar : Car, Aircraft {
}
class Transformer : FlyingCar, Ship {
override fun ride() {
println("I'm riding")
}
override fun fly() {
println("I'm flying")
}
override fun sail() {
println("I'm sailing")
}
}
fun main() {
// Transformer interface 타입의 t
val t = Transformer()
t.ride()
t.fly()
t.sail()
// Ship interface 타입의 s에 Transformer interface 타입의 t 할당
val s: Ship = t
s.sail()
// FlyingCar interface 타입의 fc에 Transformer interface 타입의 t 할당
val fc: FlyingCar = t
fc.fly()
fc.ride()
}
같은 메소드가 정의된 인터페이스들의 다중 상속
같은 메소드가 여러 인터페이스에 선언된 경우 상속 받은 클래스는 반드시 내부 구현이 존재해야한다.
즉 override 해야한다.
만일 한쪽 인터페이스에만 내부 구현이 있는 경우라도 안전성을 위해서 상속 받은 클래스는 별도의 내부 구현을 가져야한다.
< 한 쪽 인터페이스에만 내부 구현이 있는 경우 >
interface Car {
fun move() {
println("I'm moving")
}
}
interface Ship {
fun move()
}
class Amphibia : Car, Ship {
// 상속하는 인터페이스의 2개 함수 중 하나만 구현이 되어 있을 경우
override fun move() {
super.move() // 구현된 부모 인터페이스의 함수 사용
}
}
fun main() {
val a = Amphibia()
a.move()
}
< 두 인터페이스 모두 내부 구현이 있는 경우 >
interface Car {
fun move() {
println("I'm moving")
}
}
interface Ship {
fun move() {
println("I'm sailing")
}
}
class Amphibia : Car, Ship {
// 상속하는 인터페이스의 2개 함수 모두 구현이 되어 있을 경우
override fun move() {
super<Car>.move()
println("Or")
super<Ship>.move()
}
}
fun main() {
val a = Amphibia()
a.move()
}
Delegate 패턴
인터페이스에서는 함수의 구체적인 Body를 구현할 수 없다.
=> 같은 인터페이스를 상속 받는 클래스가 같은 함수를 사용할 때, 중복해서 코드를 작성해야하는 문제가 발생한다.
< Delegate 패턴을 사용하지 않은 코드 >
abstract class Terran(var xcoord: Double, var ycoord: Double) {
abstract fun die()
}
interface Flyable {
fun fly()
}
class Wraith(x: Double, y: Double) : Terran(x, y), Flyable {
override fun die() {
println("펑")
}
override fun fly() {
println("슈욱")
}
}
class 발키리(x: Double, y: Double) : Terran(x, y), Flyable {
override fun die() {
println("광")
}
override fun fly() {
println("슈욱")
}
}
< Delegate 패턴을 사용한 코드 >
abstract class Terran(var xcoord: Double, var ycoord: Double) {
abstract fun die()
}
interface Flyable {
fun fly()
}
// Delegate 클래스 생성
class FlyingDelegate : Flyable {
// 여러번 사용되는 함수를 구현
override fun fly() {
println("슈욱")
}
}
class 레이스(x: Double, y: Double) : Terran(x, y), Flyable {
override fun die() {
println("펑")
}
// Delegate Pattern => 함수 재사용
private val flyingDelegate = FlyingDelegate()
override fun fly() {
flyingDelegate.fly()
}
}
class 발키리(x: Double, y: Double) : Terran(x, y), Flyable {
override fun die() {
println("광")
}
// Delegate Pattern => 함수 재사용
private val flyingDelegate = FlyingDelegate()
override fun fly() {
flyingDelegate.fly()
}
}
'Study > Kotlin Study' 카테고리의 다른 글
[Kotlin] data 클래스 (0) | 2023.11.14 |
---|---|
[Kotlin] enum 클래스 (1) | 2023.11.13 |
[Kotlin] 추상 클래스와 추상 멤버 (0) | 2023.11.02 |
[Kotlin] 공통 메소드 (1) | 2023.10.23 |
[Kotlin] 타입 검사와 캐스팅 (2) | 2023.10.23 |