밤빵's 개발일지
[TIL]20241025 오버로딩 & 오버라이딩 본문
코틀린을 학습하면서 자바에서 접했던 오버로딩(Overloading)과 오버라이딩(Overriding) 개념을 다시 이해하게 되었다. 이 두 개념은 코틀린에서도 함수와 메서드의 재사용 및 확장성을 높이는 데 매우 중요한 역할을 한다. 이번 개발일지에서는 코틀린에서 오버로딩과 오버라이딩을 어떻게 활용하는지, 그리고 자바와는 어떤 차이가 있는지 알아보았다.
▶함수 오버로딩 (Function Overloading)
함수 오버로딩이란 같은 이름을 가진 함수가 매개변수의 타입이나 개수에 따라 다르게 동작하도록 만드는 것을 의미한다. 함수의 이름은 같지만, 서로 다른 매개변수 구성을 가지는 여러 함수를 정의할 수 있다. 이를 통해 동일한 기능을 여러 상황에 맞게 다양하게 처리할 수 있다.
fun printMessage(message: String) {
println("Message: $message")
}
fun printMessage(message: String, count: Int) {
repeat(count) {
println("Message: $message")
}
}
fun printMessage(message: String, prefix: String) {
println("$prefix: $message")
}
fun main() {
printMessage("Hello") // 기본 메시지 출력
printMessage("Hello", 3) // 메시지를 3번 출력
printMessage("Hello", "Notice") // 프리픽스와 함께 출력
}
→ 위의 예시에서 printMessage라는 같은 이름의 함수를 세 번 정의했다. 하지만 매개변수의 타입과 개수가 다르기 때문에, 각 함수는 다른 역할을 수행할 수 있다. 이를 통해 상황에 맞게 함수를 호출할 수 있고, 코드의 가독성과 재사용성을 높일 수 있다.
▶함수 오버라이딩 (Function Overriding)
오버라이딩은 상속받은 클래스에서 부모 클래스에 정의된 메서드를 재정의하는 것을 의미한다. 이를 통해 부모 클래스의 기본 동작을 자식 클래스에서 필요한 대로 변경할 수 있다. 코틀린에서 오버라이딩을 하기 위해서는 open 키워드를 사용하여 부모 클래스의 메서드를 오버라이딩 가능하도록 해야 하고, 자식 클래스에서는 override 키워드를 사용한다.
open class Animal {
open fun sound() {
println("Some generic animal sound")
}
}
class Dog : Animal() {
override fun sound() {
println("Bark")
}
}
class Cat : Animal() {
override fun sound() {
println("Meow")
}
}
fun main() {
val myDog = Dog()
val myCat = Cat()
myDog.sound() // "Bark" 출력
myCat.sound() // "Meow" 출력
}
→ Animal 클래스는 sound 메서드를 가지고 있고, 이는 기본적으로 "Some generic animal sound"라는 메시지를 출력한다. Dog와 Cat 클래스는 각각 Animal 클래스를 상속받아 sound 메서드를 오버라이딩하여 "Bark"와 "Meow"를 출력하도록 재정의했다. 이를 통해 같은 sound 메서드를 호출하더라도 객체의 타입에 따라 다른 동작을 수행하게 된다.
▶super 키워드를 활용한 오버라이딩
코틀린에서는 오버라이딩된 메서드에서 부모 클래스의 원래 메서드를 호출할 때 super 키워드를 사용할 수 있다. 이를 통해 부모 클래스의 동작을 유지하면서 필요한 부분만 추가하거나 변경할 수 있다.
open class Shape{
open fun draw(){
println("Drawing a shape")
}
}
class Circle:Shape(){
override fun draw(){
super.draw() // 부모 클래스의 draw() 호출
println("Drawing a circle")
}
}
fun main(){
val myCircle=Circle()
myCircle.draw()
}
→ Shape 클래스의 draw 메서드를 Circle 클래스에서 오버라이딩하였다. 오버라이딩된 메서드 내에서 super.draw()를 호출해서 부모 클래스의 draw 메서드를 먼저 실행한 후, 추가적인 작업("Drawing a circle")을 수행한다. 이 방식으로 부모 클래스의 기능을 유지하면서 새로운 기능을 덧붙일 수 있다.
▶코틀린에서 오버로딩과 오버라이딩의 차이
- 오버로딩: 같은 함수 이름을 사용하면서, 매개변수의 타입이나 개수를 다르게 하여 여러 함수를 정의하는 것.
- 오버라이딩: 상속받은 클래스에서 부모 클래스의 메서드를 재정의하는 것.
▶코틀린에서의 유의사항
코틀린에서는 메서드를 오버라이딩하려면 부모 클래스에서 open 키워드를 사용해야 한다. 기본적으로 코틀린의 클래스와 메서드는 final(상속 불가능)이기 때문이다. 자바와 달리 코틀린은 명시적인 override 키워드를 요구한다. 이는 오버라이딩이 의도적인 것임을 명확하게 하기 위함이다. 오버로딩 시 함수의 매개변수 타입과 개수에 따라 다른 함수를 호출하게 되므로, 함수 사용 시 매개변수에 주의해야 한다.
▶자바와의 차이점
▷오버로딩(Overloading)에서의 차이점
- 기본 인자(Default Arguments): 코틀린은 함수에 기본 인자를 사용할 수 있다. 이로 인해 자바처럼 동일한 함수 이름으로 다양한 매개변수 조합을 가지는 오버로딩을 자주 사용하지 않아도 된다. 기본 인자를 사용하면 매개변수의 값을 생략하거나 기본값을 사용할 수 있기 때문에, 코드가 더 간결해지고 오버로딩의 필요성이 줄어든다.
fun greet(name: String, greeting: String = "Hello") {
println("$greeting, $name!")
}
greet("Kotlin") // 기본 인자 사용: Hello, Kotlin!
greet("Kotlin", "Hi") // 오버로딩처럼 동작: Hi, Kotlin!
▷자바에서는 기본 인자를 사용할 수 없으므로 오버로딩으로 처리
void greet(String name) {
greet(name, "Hello");
}
void greet(String name, String greeting) {
System.out.println(greeting + ", " + name + "!");
}
▷오버라이딩(Overriding)에서의 차이점
- Override 강제 키워드 사용: 코틀린에서는 메서드를 오버라이딩할 때 override 키워드를 반드시 사용해야 한다. 이는 자바의 선택적 @Override 애너테이션과 다르다. override 키워드 덕분에 의도적으로 오버라이딩하는 것임을 명시적으로 알릴 수 있고, 실수로 오버라이딩하지 않는 경우 컴파일러가 오류를 발생시킨다.
open class Animal{
open fun sound(){
println("Some sound")
}
}
class Dog:Animal(){
override fun sound(){
println("Bark")
}
}
▷자바에서는 @Override 어노테이션이 선택적
class Animal {
void sound() {
System.out.println("Some sound");
}
}
class Dog extends Animal {
@Override
void sound() {
System.out.println("Bark");
}
}
▷super 키워드 사용
코틀린과 자바 모두 오버라이딩된 메서드에서 부모 클래스의 메서드를 호출할 때 super 키워드를 사용한다. 하지만 코틀린에서는 super를 더 유연하게 사용할 수 있다. 예를 들어, 다중 상속(인터페이스 다중 구현) 상황에서 특정 부모 클래스의 메서드를 호출할 때 super<부모클래스명>.메서드() 형식으로 지정할 수 있다.
interface A {
fun show() {
println("From A")
}
}
interface B {
fun show() {
println("From B")
}
}
class C : A, B {
override fun show() {
super<A>.show() // A의 show() 호출
super<B>.show() // B의 show() 호출
}
}
▷자바에서는 다중 상속이 불가능하고, 인터페이스의 super 호출은 제한적
interface A {
default void show() {
System.out.println("From A");
}
}
interface B {
default void show() {
System.out.println("From B");
}
}
class C implements A, B {
@Override
public void show() {
A.super.show(); // A의 show() 호출
B.super.show(); // B의 show() 호출
}
}
▷가시성 제어자
코틀린에서는 함수 오버라이딩 시 가시성 제어자(예: public, protected)를 변경할 수 없다. 이는 기본적으로 부모 클래스의 접근 수준을 유지해야 한다는 원칙에 기반한다. 자바에서는 부모 클래스 메서드보다 더 넓은 범위의 가시성을 가질 수 있지만, 코틀린은 이를 엄격하게 제한하여 코드의 안전성을 강화한다.
코틀린에서 함수 오버로딩과 오버라이딩은 자바와 개념적으로 유사하지만, 기본 인자, 엄격한 override 키워드 사용, 그리고 가시성 제어 등에 있어 차이점이 있다. 코틀린의 엄격한 타입 시스템과 키워드(open, override)의 사용으로 인해 더 명확하고 안전하게 코드를 작성할 수 있다. 또한, super 키워드를 통해 부모 클래스의 기능을 쉽게 재사용할 수 있어 코드의 유연성이 높아진다. 이러한 코틀린의 설계는 코드의 가독성을 높이고 실수를 방지하기 위한 장점으로 작용한다.
'Kotlin' 카테고리의 다른 글
[TIL]20241027 sealed class 와 Enum (0) | 2024.10.27 |
---|---|
[TIL]20241026 apply, with, run (2) | 2024.10.26 |
[TIL]20241024 Type-Safe Builder (0) | 2024.10.24 |
[TIL]20241023 Infix Function (3) | 2024.10.23 |
[TIL]20241022 단일 표현식 함수(Single-Expression Function) (2) | 2024.10.22 |