24-04-26 Kotlin 인터페이스와 추상 클래스
오늘은 이번주 초에 주어졌더 계산기 만들기 과제에 대한 해설 강의가 있었다. 객체 지향 프로그래밍에 대한 이해에 큰 도움을 받을 수 있는 강의였다.
인터페이스, 추상 클래스를 직접 사용해서 계산기를 만들어는 보았지만 왜 이걸 사용해야하는지에 대해서는 잘 알지 못했다. 사용법은 알지만 사용하는 이유를 몰랐던 것이다. 이런 의문들을 어느정도 해소할 수 있어서 매우 좋은 시간이었다.
그리고 거의 똑같다고 생각했던 인터페이스와 추상 클래스에 대해 조금 더 찾아보고 정리해보았다.
1. 인터페이스와 추상 클래스의 차이
인터페이스와 추상 클래스의 공통점
- 추상 클래스는 구현되지 않은 클래스이기 때문에, 인터페이스는 클래스가 아니기 때문에 이 둘을 통해서는 모두 객체를 생성할 수 없다.
- 둘을 상속받은 클래스는 반드시 안에 있는 추상 메서드를 구현해야한다.
인터페이스가 추상 클래스와 다른 점
- 코틀린은 다중 상속을 지원하지 않기 때문에 객체를 객체 자신의 타입이나 부모의 타입에만 담을 수 있다. 그렇지만 인터페이스를 활용하면 생성된 객체의 주소 값을 다양한 타입의 변수에 담을 수 있다.
- 클래스는 한 개 이상의 인터페이스를 구현할 수 있다. 이 인터페이스들을 구현한 클래스 타입의 객체는 구현한 인터페이스형 참조 변수에 담을 수 있다.
- 추상 클래스와 거의 비슷하지만 하나의 클래스에 여러 인터페이스를 구현할 수 있다는 점이 다르다.
아래처럼 직접 코드를 작성하여 두 개의 인터페이스를 구현한 자식 클래스를 만들어보았다. 2개의 인터페이스 타입의 클래스를 인수로 받는 함수를 각각 만들어 2개의 함수를 만들었다. 그 다음 인터페이스를 구현한 자식 클래스 타입의 객체를 만든 뒤 이 두 함수의 매개변수로 넣어 둘다 잘 작동하는지 확인해보았다.
fun main() {
val sub = SubClass() // 자식 클래스의 객체
inter1(sub)
/* Interface1의 interface1Method1
SubClass의 interface1Method2 */
inter2(sub)
/* Interface2의 interface2Method1
SubClass의 interface2Method2 */
}
fun inter1(obj:Interface1) {
obj.interface1Method1()
obj.interface1Method2()
}
fun inter2(obj:Interface2) {
obj.interface2Method1()
obj.interface2Method2()
}
interface Interface1 {
fun interface1Method1() {
println("Interface1의 interface1Method1")
}
fun interface1Method2()
}
interface Interface2 {
fun interface2Method1(){
println("Interface2의 interface2Method1")
}
fun interface2Method2()
}
class SubClass : Interface1, Interface2 {
override fun interface2Method2() {
println("SubClass의 interface2Method2")
}
override fun interface1Method2() {
println("SubClass의 interface1Method2")
}
}
추상클래스와 인터페이스의 차이
위와같이 사용하는 방법에서의 차이말고 둘을 언제 사용해야하는 지에 대한 차이에 대해 생각해봐야한다.
추상 클래스의 경우는 상속으로 강하게 얽매여있다. 여러가지 클래스들을 일반화하여 하나로 묶은 것이 추상 클래스이다. 예를들어 개, 고양이, 사람, 상어, 거북이, 사자, 말 모두 동물이다. 거미, 개미, 파리, 풍뎅이는 모두 곤충이다. 이 동물들과 곤충들은 각각 모두 공통된 특징이 있을 것이고 이것을 기준으로 하나로 일반화하여 묶은것이 추상 클래스(동물, 곤충)이라고 생각해볼 수 있다.
인터페이스의 경우에는 여러 클래스들을 어떠한 행위 다시 말해 구현하여 생성한 객체가 모두 같은 동작을 한다는 것을 보장하기 위해 사용하는 것이다. 위와 똑같은 예를 들어보자. 동물들 중에는 상어, 거북이, 참치만 물에서 숨을 쉴 수 있다. 그리고 개, 고양이, 사람, 사자, 말, 거북이만이 물 밖에서 숨을 쉴 수 있다. 곤충들 중에는 거미, 개미, 파리, 풍뎅이가 모두 물 밖에서 숨을 쉴 수 있다.
이런 경우 우리는 육지에서 숨을 쉴 수 있다라는 같은 동작을 한다라는 기준으로 개, 고양이, 사람, 사자, 말, 거북이, 거미, 개미, 파리, 풍뎅이를 하나로 묶을 수 있을 것이다. 그리고 상어, 거북이, 참치를 물에서 숨을 쉴 수 있다라는 기준으로 또 묶을 수 있다. 이때 거북이처럼 둘 다에 속하는 것도 생기게 된다.
여기서 숨을 쉴 수 있다라는 동작을 한다는 기준으로 묶은 것들은 무엇보다도 동물, 곤충 완전히 다른 종류(클래스)에 속하는 이들을 같은 동작을 한다라는 기준으로 묶을 수 있었다라는 것에 주목해야한다. 즉 인터페이스를 사용하면 상속과 관계에 얽매히지 않고 자유롭게 다형성을 구현할 수 있다.
2. 오늘 배운 것
- 인터페이스, 추상 클래스, 의존성 주입에 대해 알게 되었다.
- 의존성 역전 원칙에 따라 개방 폐쇄의 원칙에 따라 프로그래밍 한다는 것이 어떤 것인지 어느 정도 감을 잡게 된 것 같다. 물론 실제로 코드로 작성하는 것은 많은 어려움이 따를 것 같다.