본문 바로가기
언어/Kotlin

[Kotlin 문법 종합] - 오버라이딩, 오버로딩 # 수정하기

by 젼젼39 2024. 3. 5.

 

 

1. 오버라이딩

    : 상속받은 부모클래스의 정보(프로퍼티)나 행위(메소드) 재설계
    = 리디파인...?

    - 받는 인자의 개수, 타입 같음
    - 부모 클래스의 메소드와 같은 이름을 가진 하나의 함수만을 가짐
          = 부모 클래스의 메소드와는 다른 메소드가 된 것  

    (+) 자식 클래스만의 정보, 동작을 만들 수 있음
    (+) 재사용성, 일관성 등의 장점 유지

    *오버라이딩 단축키
        Control + O

fun main() {
    var bird = Bird("새")
    var chicken = Chicken("닭", 2)

    bird.fly()
    chicken.fly()
}

open class Bird(name:String) { 		//open 작성
    var name: String = ""

    init {
        this.name = name
    }

    open fun fly() { 		//메소드에서도 open을 써야하는듯
        println("${name}은 날아요~")
    }
}

class Chicken(name: String, age: Int) : Bird(name) {
    var age:Int = 0

    init { 		//부모클래스에 init 있으니 여기도..?
        this.age = age
    }

    override fun fly() {
//        부모객체의 fly메소드를 부르는 행위임. 이걸 포함하려면 작성하기
//        super.fly() 		
        println("${age}살의 ${name}가 날아봅니다~ 꼬끼오!")
    }
}

   

*super 안 쓰는 이유를 더 찾아봤는데

        // super.메서드이름() --> 내부적으로 상속받으면 자동으로 만들어지는 부모객체. = 부모객체의 메서드를 불러라
        //그럼 오버로딩 후에는 super 쓰나? 확인하기@@@ --> 딱히 쓰지 않아도... 다른 메소드가 있더라. 자바로 바꾸면 나타나던데

코틀린에서는 `super` 키워드를 사용하여 부모 클래스의 생성자나 메서드를 호출할 필요가 없는 경우가 있는데, 
그 이유는 다음과 같습니다:

1. **기본 생성자 호출**: 
  자식 클래스가 부모 클래스를 상속받을 때, 부모 클래스의 기본 생성자가 호출됩니다.
  이는 코틀린에서 기본적으로 제공되는 동작입니다.
  따라서 대부분의 경우에는 명시적으로 `super` 키워드를 사용하지 않아도 됩니다.

2. **메서드 오버라이딩**:
  부모 클래스의 메서드를 자식 클래스에서 오버라이드할 때에도 `super` 키워드를 사용하지 않아도 됩니다.
  부모 클래스의 메서드를 호출하려면 `super`를 사용할 수 있지만,
  그렇지 않은 경우에는 자식 클래스에서 오버라이드된 메서드가 호출됩니다.

3. **부모 클래스 생성자 호출**:
  만약 자식 클래스가 부모 클래스의 생성자를 오버라이드하거나 추가적인 생성자를 정의할 때에는
  `super` 키워드를 사용하여 부모 클래스의 생성자를 명시적으로 호출해야 합니다.
  하지만 코틀린에서는 생성자 호출을 간단히 처리하기 때문에,
  `super` 키워드를 사용하지 않고도 부모 클래스의 생성자가 자동으로 호출됩니다.

결론적으로, 코틀린은 명시적인 `super` 호출을 최소화하여 코드를 간결하게 만들 수 있도록 설계되었습니다.
따라서 대부분의 경우에는 `super`를 사용하지 않아도 됩니다.

       (...라는데 이건 조금 더 비교해서 정리해야할 듯. 자바랑 c++이랑 다 섞여서 뒤죽박죽이네)

 

    - object 표현식을 사용해 하위 클래스 만들지 않고 클래스의 특정 메서드 오버라이딩하기

        //익명 객체 : 이름이 없는 클래스 인스턴스 만들기.

open class Hero(){
    fun work() = println("working")
    open fun fly() = println("flying")
}

fun main(){
    val person = object: Hero(){ 	//object 표현식으로 fly() 구현의 재정의
        override fun fly() = println("not flying")
    }
    person.work()
    person.fly()    
}

//working
//not flying

        (1) 활용사례 1

window.addMouseListener(object: MouseAdapter){
    override fun mouseClicked(e: MouseEvent){
        ...	
    }
    override fun mouseEntered(e: MouseEvent){
        ...
    }
}


    - object: MouseAdapter라는 구문은 MouseAdapter 클래스를 상속하는 익명 객체를 생성.
                (MouseAdapter 클래스는 MouseListener 인터페이스의 구현을 제공하는 추상 클래스)

익명 객체를 생성함으로써, 필요한 메서드만 오버라이드하여 해당 이벤트를 처리할 수 있음.
(위의 코드에서는 mouseClicked와 mouseEntered 메서드를 오버라이드하여 각각 마우스 클릭 및 진입 이벤트를 처리함)

MouseListener 인터페이스를 직접 구현하는 대신 익명 객체를 사용하여 더 간결하게 코드를 작성할 수 있음

        (2) 활용사례 2 - 단 한 번 사용할 인터페이스의 구현 클래스 정의 없이 사용하기

interface Shape{
    fun onDraw() 		//구현해야 할 메서드
}

val triangle = object: Shape{
    override fun onDraw(){
        //여기서 단 한 번 구현함
    }
}

        (3) 활용사례 3 - 객체는 필요하지만 상위 인터페이스, 클래스가 없는 경우

fun foo(){
    val adHoc = object{
        var x: Int = 0
        var y: Int = 0
    }
    print(adHoc.x + adHoc.y)
}

 

 

2. 오버로딩

    : 매개변수의 개수, 자료형을 다르게 해 동명의 다른 메소드를 만든다
        //반환자료형은 오버로딩에 영향을 주지 않음

    - 받는 인자, 개수 등이 달라짐
          //매개변수의 개수 등에 따라 어느것이 쓰일지가 결정됨
    - 상속 관련 --> 자식 클래스는 부모 클래스의 함수와 같은 이름을 가진 두 개의 함수를 가짐
          //하나는 부모 클래스에 정의되어있고, 나머지는 자식 클래스에 정의됨
    

fun main() {
    var calc = Calculator() 	//객체 만들기
    
    var intResult = calc.add(1,2)
    var doubleResult = calc.add(1.2, 2.2)
    
    println("정수 덧셈결과: ${intResult}")
    println("실수 덧셈결과: ${doubleResult}")
    
}

class Calculator {
    
    fun add(num1: Int, num2: Int): Int {
        return num1+num2
    }
    
    fun add(num1: Double, num2: Double): Double {
        return num1+num2
    }
}
class Point(var x: Int = 0, var y: Int = 10) {
    // plus 함수의 연산자 오버로딩
    operator fun plus(p: Point) : Point {
        return Point(x + p.x, y + p.y)
    }
    operator fun dec() = Point(--x, --y)
}

fun main() {
    val p1 = Point(3, -8)
    val p2 = Point(2, 9)

    var point = Point()
    println(point.y)
    println(p1.x)
    point = p1 + point
    println("point = (${point.x}, ${point.y})")
    point = p1 + p2 // Point 객체의 + 연산이 가능하게 되었다.
    println("point = (${point.x}, ${point.y})")
    --point // -- 연산자
    println("point = (${point.x}, ${point.y})")

}


10
3
point = (3, 2)
point = (5, 1)
point = (4, 0)