
init() 은 여러 개 있을 수 있고, 순차적으로 실행된다.val animal = OrdAnimal()
animal.eat("aa")
val playWithAnimal = animal.play()
playWithAnimal()
open fun eat(sth: String) = println("${sth}을/를 먹습니다!")
open fun play() : ()-> Unit = {
println("동물이 뛰어다닙니다!")
println("즐거워합니다.")
}
eat과 같이 사용할 수 있다. play()는 함수 호출이 아닌, 함수를 반환하게 된다. 기존 클래스(부모 클래스 또는 슈퍼클래스)의 속성과 메서드를 새로운 클래스(자식 클래스 또는 서브 클래스)에서 물려받아 사용하는 메커니즘이다. 상속을 통해 자식 클래스는 부모 클래스에서 정의된 모든 속성과 메서드를 그대로 사용할 수 있으며, 필요한 경우 이를 확장하거나 재정의(오버라이딩)할 수 있다. 따라서, 상속은 다형성을 구현하는 데에 중요한 역할을 한다.
상속은 객체 지향 설계의 원칙인 개방-폐쇄 원칙(Open-Closed Principle)을 구현하는 데에 중요한 도구이다.
open class Parent {
}
class Child : Parent() { // 부모 클래스의 생성자를 호출한 것
}
final이 다 붙여져있다.open 변경자를 붙여야 한다. 같은 이름의 함수를 매개변수의 타입이나 개수만 다르게 하여 여러 개 정의하는 것을 말한다.
상위 클래스 (또는 인터페이스)에 정의된 메서드를 하위 클래스에서 재정의하는 것을 의미한다.
open 키워드와 override 키워드를 통해 수행된다.open class Dog {
fun eat(food:String) : Unit {
println("${food}을/를 먹습니다.")
}
open fun bark(int times) : Unit {
for( n in 1..times) {
print("멍!")
}
println()
}
}
class Husky : Dog() {
override fun bark(times:Int) : Unit {
for(n in 1..times) {
print("멍멍!")
}
}
}
open 변경자를 붙여야 합니다.코틀린에서는 연산자도 재정의할 수 있다.
fun main() {
val coffee1 = OpCoffee("아메리카노", "")
val coffee2 = OpCoffee("카페라떼", "")
val result: String = coffee1 + coffee2
println("result = ${result}") // 첫번째 커피 : 아메리카노, 두번째 커피 : 카페라떼
}
class OpCoffee(val type: String, val size:String) {
operator fun plus(other: OpCoffee) : String {
return "첫번째 커피 : ${this.type}, 두번째 커피 : ${other.type}"
}
}
operator 라는 키워드를 붙여주고 연산자를 사용하면 된다. plus와 같이 고정된 이름을 사용해주면 되며, 이외에는 plusAssign (+=), minus(-), times(*), div(/), rem(%), compareTo(>), get(인덱싱), contains(in) 의 연산자를 오버라이딩할 수 있다. 상위 클래스 (또는 상위 인터페이스)의 멤버에 접근할 때 사용된다.
super()를 별도로 쓰지 않고 상속 구문에서 바로 인자를 전달하는 방식이 일반적이지만, 보조 생성자에서는 super(...)를 명시적으로 사용할 수 있다. super()는 상속받은 클래스가 초기화되기 전에 반드시 상위 클래스가 먼저 초기화되어야 한다는 Kotlin의 규칙을 따르기 위해 필요하다. class Husky : Dog() {
override fun eat(food: String) : Unit {
super.eat(food)
println("멍멍!")
}
}
클래스의 기본 형태를 정의하고, 이를 상속받아 구체적인 구현을 제공할 수 있도록 하는 클래스이다. 추상 클래스는 상속을 통해 공통적인 기능을 제공하면서, 자식 클래스에서 구체적인 동작을 정의할 수 있는 기반을 마련한다.
{})가 없다. abstract class 클래스_이름 {
}
open으로 열어주지 않은 메서드는 오버라이드할 수 없다. abstract 키워드가 붙여져있으면, open하지 않아도 오버라이드할 수 있다. abstract가 붙여져있더라도, 오버라이드할 때는 override 키워드를 붙여줘야 한다.클래스가 구현해야 하는 메서드의 집합을 정의하는 일종의 클래스를 위한 설계도이다. 인터페이스는 메서드의 시그니처(이름, 반환형, 매개변수)만을 정의하며, 메서드의 실제 구현은 이를 구현하는 클래스에서 수행해야 한다.
❓ 추상 클래스 vs 인터페이스
둘 다 객체 지향 프로그래밍에서 다형성을 지원하는 수단이지만, 추상 클래스는 구현된 메서드와 필드를 가질 수 있다. 반면, 인터페이스는 메서드의 시그니처만을 정의한다. 또, 인터페이스는 다중 상속을 통해 여러 기능을 클래스에 제공하는 역할에 중점을 두지만, 추상 클래스는 단일 상속만 가능하고, 클래스 간의 공통된 동작을 제공하는 데 주로 사용된다.
interface Car {
fun drive()
fun reverse()
fun onTurnSignal()
}
public, abstract를 포함하고 있기 때문에, 명시하지 않아도 무관하다.public, static, final로 선언된다.class Sedan : Car {
override fun drive() {
println("주행합니다.")
}
override fun reverse() {
println("후진합니다.")
}
override fun onTurnSignal() {
println("방향지시등을 켭니다.")
}
}
abstract를 포함하고 있기 때문에, 오버라이드하기 위해 open을 쓰지 않아도 되지만, 구현체에서는 override를 붙여줘야 한다.기존 클래스(내가 만든 클래스든, 라이브러리 클래스든)를 수정하지 않고도 마치 그 클래스의 메서드인 것처럼 새로운 함수를 "확장"해서 추가할 수 있다. 확장 함수는
클래스타입.함수이름형식으로 선언하며, 해당 클래스의 인스턴스에서 바로 사용할 수 있다.
val sentence = "Hello World!"
sentence.printUpperCase() // HELLO WORLD!
fun String.printUpperCase() {
println(this.uppercase())
}
fun Int.customSum(target: Int) {
println("두 값을 더하면 ${this + target}")
}
fun Int.printPrice() {
println("가격가격")
}
public이다. 오늘은 집중도 잘됐고, 시간도 잘 흘렀던 것 같다. 몸이 다시 수업에 적응해가고 있는 것 같다! 아주 좋아 !
그리고 코틀린 재밌다 ! 신기한 기능이 너무 많다. 확장 함수 아주 흥미롭고, 상속이랑 구현하는 방식도 아주 흥미롭다. 코드 짤 때 override, open 이런 거 안 까먹고 잘 챙겨야 할 텐데 ㅎ하하하
그리고 오늘 점심시간 때 우수 참여자와 우수 출결자를 뽑아서 알려주셨는데,, ! 우수 참여자에 뽑혔다 !!! 너무 뿌듯하고, 기분이 좋잖아.. ㅠㅠ 열심히 들으려고 노력했는데, 그걸 알아봐주셔서 정말 감사합니다.. 더 열심히 해야지.. !! 근데 진짜 우수 출결자들 대단하신 것 같다. 아무튼 뿌듯한 날이다 ! 🥹🔥
수요일 수업도 잘 끝나가고 있다.. ! 내일, 모레 이틀만 버티자.. 아자 아자 !!
아 그리고, 오늘 마치고 할머니댁 가서 소고기 먹는다. 아싸.