코틀린의 클래스는 기본적으로 final입니다. 따라서 상속이 불가능한 형태로 되어 있습니다.
이는 컴파일 과정에서 또다른 최적화의 요소로 반영됩니다. 물론 open
이라는 지시어를 통해 상속가능한 형태로 바꿔 줄 수 있습니다.
인터페이스
기본적으로는 자바의 그것과 동일합니다. 8버전이상의 자바에서 지원되는 default method 역시 별도의 지시어 없이 선언 가능합니다.
1 | interface Accessable<T> { |
static 요소가 필요한 경우 내부에 컴패니언 객체를 선언하여 사용 가능합니다.
추상 클래스
추상 클래스도 마찬가지로 자바의 그것과 동일합니다.
제약 조건역시 동일합니다. 추상클래스는 클래스이기 때문에 한가지만 상속가능합니다.
1 | abstract class Base(val id: String) { |
인터페이스 vs 추상 클래스
기본적으로는 동일하지만 여러개를 상속(구현)할 수 있는지, 또한 필드를 가질 수 있는지 없는지에 따라 선택하게 됩니다. 일반적인 객체지향 설계에서는 상속보다는 구성들을 통한 조합을 더 선호하기 때문에 기능의 사용 가능 여부를 나타내는 인터페이스가 대부분의 경우에 맞겠지만 어떤 도메인에 일정한 필드들이 공통된 값을 가지고 존재해야 하는 경우라면 추상 클래스를 상속해야 할 것 입니다.
중첩 클래스와 내부 클래스
중첩 클래스는 클래스 내부에 클래스를 생성하는 것으로 기본적으로는 자바의 static inner class로 생성됩니다. static이기 때문에 내부 정보를 공유할 수 없습니다. 만약 자바의 기본 inner class가 필요한 경우엔 inner라는 지시어를 class 앞에 추가하면 됩니다.
1 | class Outer { |
상속
코틀린의 모든 클래스는 Any 라는 클래스를 상속받습니다. 이는 명시적으로 지정하지 않아도 알아서 상속이 됩니다.
Any 는 자바의 Object처럼 equals, hashCode, toString를 가지고 있습니다.
기본적으로 코틀린은 상속을 허용하지 않습니다. 무분별한 상속 구현을 막고 내부 바이트코드 상으로도 약간의 최적화를 가지는 효과를 가집니다. 정말 상속이 필요한 클래스라면 open 이라는 키워드를 추가함으로써 정의 가능합니다.
1 | open class OS(val name: String, open val arch: String = "X86") { |
필드 역시 상속이 가능한데 필드가 val 로 선언된 경우 val, var로 오버라이딩 할 수 있고 var은 var로만 오버라이딩 가능합니다.
오버라이드를 할 때는 override 지시어를 추가해야합니다. 예제에서는 toString 에 대해 오버라이딩을 했는데, 코틀린에서 override 를 추가한 경우 해당 메소드는 자동적으로 open 상태이기 때문에, 만약 자식클래스에서의 오버라이딩을 원하지 않을 경우 final 을 추가하여 자바처럼 상속불가능하게 선언 가능합니다. 그리고 자바에서 @Override 는 개발자가 추가했을때 컴파일 타임에 검사를 하는 형태인데, 코틀린의 경우 override 가 없으면 무조건 실패하기 때문에, 실제 이 오버라이딩이 맞는지 확인 할 수 있다는 장점이 있습니다.
1 | class Linux(arch: String): OS("Linux", arch) { |
위 Linux 클래스는 OS라는 클래스를 상속한 클래스로 상속할 때 자바처럼 extends 나 implement 같은 지시어가 없이 : 만으로 상속을 선언합니다. 또한 위와 같이 클래스를 상속 받을 때는 부모클래스의 이름 뒤에 ()를 붙여 생성자를 실행하도록 해야합니다. 따라서 Linux 클래스도 OS의 생성자를 호출 하도록 했습니다.
추가로 arch 정보를 오버라이딩하여 setter를 수정하였고 빈 값은 들어올 수 없도록 설정했습니다.
Sealed 클래스
sealed 클래스는 재미있는 클래스 입니다. abstract 처럼 실제 객체를 가질 수 없는 오직 상속만을 위한 클래스지만 상속받을 수 있는 범위가 제한되어 있습니다. 일반적인 abstract 클래스는 일반적으로 접근제어자에 맞는 범위내에서 무제한으로 상속받을 수 있으나 sealed 클래스는 다릅니다. sealed 클래스를 정의한 파일에서만 상속을 받을 수 있습니다. 그로 인해 원하는 만큼 상속받는 자식 클래스를 제한 할 수 있습니다.
1 | sealed class Either<out L, out R> { |
이전에 확인했던 Either 클래스 입니다. Either 클래스는 flatMap이라는 메소드를 가지고 있고 메소드의 내부를 보면 when 문을 이용해 자식 클래스 타입에 따라 실행을 분기합니다. 원래였다면 else 절이 있어야겠지만, sealed 클래스는 외부에서 상속을 받을 수 없기 때문에, 같은 파일 내에 정의된 자식 클래스 Right, Left 만 가지게 되고 이로 인해 위와 같이 구현이 가능했습니다.
그리고 sealed 클래스의 생성자는 private 이기 때문에, sealed 클래스를 직접 사용하는건 불가능합니다. 다만 object 등을 통해서 싱글톤 객체로 사용은 가능합니다.
Enum
자바의 enum 과 동일합니다. 특정 상태/데이터의 타입들을 나타내기 좋으며, 하나의 클래스의 제한된 객체들의 사용을 보장합니다. 상태값과 메소드를 가질 수 있으며, 원하면 인스턴스 선언 과정에서 상속(인터페이스 포함)하여 사용 할 수도 있습니다.
위임처리
객체지향의 디자인패턴에는 위임(delegation)을 통해 특정 객체가 받아 다른 객체로 넘겨주는 경우가 있습니다.
일반적인 경우라면 인터페이스를 구현하여 해당 메소드들을 일일히 코드로 표현해야 했지만 코틀린은 언어에서 이를 지원합니다.
1 | interface Runnable { |
출처
- 다재다능 코틀린 프로그래밍
- 공식문서