출처: https://www.boostcourse.org/mo234/lecture/154291?isDesc=false
ex) abstract class Vehicle ('탈 것'은 추상적이지만, 그 구체적인 예시로는 자동차, 오토바이, 배 등이 있다.)
package chap03.section1
abstract class Vehicle(val name: String, val color: String,
val weight: Double) {
// 추상 프로퍼티 (하위 클래스에서 오버라이딩 필수)
abstract val maxSpeed: Double
// 비추상 프로퍼티
var year: String = "2008"
// 추상 메서드 (오버라이딩 필수)
abstract fun start()
abstract fun stop()
// 비추상 메서드 (구현부를 가짐)
fun displaySpec(){
println("name: $name, color: $color, weight: $weight, " +
"year: $year, maxSpeed: $maxSpeed")
}
}
class Car(name: String,
color: String,
weight: Double,
override val maxSpeed: Double) : Vehicle(name, color, weight) {
override fun start() {
println("Car Started")
}
override fun stop() {
println("Car Stopped")
}
fun autoPilotOn(){
println("Auto Pilot ON")
}
}
class Motorbike(name: String,
color: String,
weight: Double,
override val maxSpeed: Double) : Vehicle(name, color, weight) {
override fun start() {
println("Motorbike Started")
}
override fun stop() {
println("Motorbike Stopped")
}
}
fun main() {
//var v = Vehicle("Matiz", "red", 1000.0) // 인스턴스 생성 불가
val car = Car("Matiz", "red", 1000.0, 100.0)
val motor = Motorbike("Motor1", "yellow", 1000.0, 100.0)
car.year = "2014"
car.displaySpec()
motor.displaySpec()
car.start()
motor.start()
}
object 키워드를 사용하면, 추상 클래스로부터 하위 클래스를 생성하지 않고도 단일 인스턴스로 객체를 생성할 수 있다.
abstract class Printer { // 추상 클래스
abstract fun print() // 추상 메서드
}
// 하위 클래스 생성하지 않고 임시 객체 생성 가능!
val myPrinter = object: Printer() {
override fun print() {
println("출력합니다.")
}
}
fun main(){
myPrinter.print()
}
인터페이스는 현실 세계에서 일종의 계약서와 같다. 계약서에는 무엇을 하라는 추상적인 활동들이 적혀있다. 이것을 어떤 '작업자'가 받아들였다면, 계약서에 있는 활동을 반드시 실행해야 한다. 계약서 자체로는 실행될 수 없다.
cf) 추상 클래스와의 차이점
다른 언어와는 다르게, 인터페이스에 기본적인 구현 내용이 포함될 수 있다. (자바에서는 버전 8부터 default 키워드를 사용해야 내용을 구현할 수 있음.)
package chap03.section2
interface Pet {
var category: String // abstract 키워드가 없어도 기본적으로 추상 프로퍼티
fun feeding() // 추상 메서드
fun patting(){ // 일반 메서드
println("Keep patting!")
}
}
class Cat(override var category: String) : Pet {
override fun feeding() { // 오버라이딩 필수
println("Feed the cat a tuna can!")
}
}
fun main() {
val obj = Cat("small")
println("Pet Category: ${obj.category}")
obj.feeding() // 구현된 메서드
obj.patting() // 기본 메서드
}
인터페이스에서는 프로퍼티에 값을 저장할 수 없지만, val로 선언한 프로퍼티는 게터를 통해 필요한 내용을 구현할 수 있다.
package chap03.section2
interface Pet {
var category: String // abstract 키워드가 없어도 기본적으로 추상 프로퍼티
val msgTags: String
get() = "I'm your lovely pet!"
fun feeding() // 추상 메서드
fun patting(){ // 일반 메서드
println("Keep patting!")
}
}
class Cat(override var category: String) : Pet {
override fun feeding() { // 오버라이딩 필수
println("Feed the cat a tuna can!")
}
}
fun main() {
val obj = Cat("small")
println(obj.category)
println(obj.msgTags)
obj.feeding() // 구현된 메서드
obj.patting() // 기본 메서드
}
클래스는 기본적으로 다중 상속을 지원하지 않는다. 반면에, 인터페이스는 여러 개를 하나의 클래스에서 구현할 수 있으므로 다중 상속과 같은 효과를 가진다.
package chap03.section2
interface Bird {
val wings: Int
fun fly()
fun jump() = println("bird jump!")
}
interface Horse {
val maxSpeed: Int
fun run()
fun jump() = println("horse jump! $maxSpeed")
}
class Pegasus: Bird, Horse {
override val wings: Int = 2
override val maxSpeed: Int = 100
override fun fly() = println("Fly!")
override fun run() = println("Run!")
override fun jump() {
super<Bird>.jump()
super<Horse>.jump()
println("and Jump!")
}
}
fun main() {
val p = Pegasus()
println("${p.wings} ${p.maxSpeed}")
p.fly()
p.run()
p.jump()
}
package chap03.section2
interface A {
fun functionA()
}
interface B {
fun functionB()
}
class C(val a: A, val b: B){
fun function(){
a.functionA()
b.functionB()
}
}
class DelegatedC(a: A, b: B): A by a, B by b {
// A와 B를 위임 받은 클래스는
// 그들의 메서드를 자신의 것처럼 사용 가능
fun functionC(){
functionA() // A의 위임
functionB() // B의 위임
}
}
package chap03.section2
interface Nameable {
var name: String
}
class StaffName: Nameable {
override var name: String = "Sean"
}
class Work: Runnable {
override fun run() {
println("work...")
}
}
class Person(name: Nameable, work: Runnable):
Nameable by name, Runnable by work
fun main() {
val person = Person(StaffName(), Work())
// 위임 받은 인터페이스의 프로퍼티 및 메서드에 바로 접근
println(person.name)
person.run()
}