질문 배경:
메서드 재정의는 OOP의 기초이지만, Java에서는 원래 override가 필수가 아니었습니다. Kotlin은 코드의 명확성을 높이고 상속 시 우연한 오류를 피하기 위해 override 사용을 필수로 하는 엄격한 타입 언어입니다.
문제:
명시적으로 override를 지정하지 않으면 인터페이스/클래스를 설명할 때 이름, 타입 또는 수정자에 오타가 쉽게 발생할 수 있습니다. 프로그램이 중단되지는 않지만 기대하는 재정의가 작동하지 않습니다. Kotlin은 이 문제를 근본적으로 해결합니다.
해결책:
Kotlin에서는 메서드를 재정의할 수 있도록 하려면 해당 메서드를 open(또는 abstract/interface)으로 선언해야 합니다. 재정의를 위해서는 반드시 override를 작성해야 합니다. 시그니처가 일치하지 않으면 컴파일 오류가 발생합니다. 이는 대부분의 전형적인 Java 버그를 예방합니다.
코드 예제:
open class Base { open fun greet() { println("Hello!") } } class Child : Base() { override fun greet() { println("Hi!") } }
주요 특징:
override를 잊으면 우연히 함수를 재정의할 수 있나요?
아니요. override를 놓치면 선언된 메서드는 클래스의 새로운 메서드로 간주되며 재정의가 발생하지 않습니다. 만약 base 메서드에 open이 있을 경우 컴파일러는 오류를 발생시킵니다.
코드 예제:
open class A { open fun test() {} } class B : A() { fun test() {} // 재정의하지 않음! test2()와 동일함. }
자신의 메서드가 부모보다 더 엄격한 가시성을 가질 수 있습니까?
아니요. 재정의되는 메서드의 가시성 범위를 좁힐 수 없습니다. 예를 들어, 기본 메서드가 public인 경우, 재정의는 private/protected/internal일 수 없습니다. 컴파일러는 오류를 발생시킵니다.
재정의된 구성 요소가 final일 수 있나요?
네! 더 이상의 재정의를 원하지 않으면 final을 추가하십시오:
class D : Base() { final override fun greet() { //... } }
Java에서:
public class Parent {void foo(){} } public class Child extends Parent {void foo(int x){} }
재정의가 기대되지만 실제로는 오버로딩입니다. 호출은 잘못된 메서드로 진행됩니다.
장점:
단점:
Kotlin에서:
open class Parent { open fun foo() {} } class Child : Parent() { override fun foo() { /*...*/ } }
장점:
단점: