들어가기 전에

본 포스팅은 개발자를 위한 코틀린 프로그래밍의 chapter단위로 공부하고, 정리, 부족한 내용의 추가 학습내용을 정리하는 블로깅입니다.
이전 포스팅 ⬇️
[chapter08] 컬렉션 알아보기
들어가기 전에 본 포스팅은 개발자를 위한 코틀린 프로그래밍의 chapter단위로 공부하고, 정리, 부족한 내용의 추가 학습내용을 정리하는 블로깅입니다. 이전 포스팅 ⬇️ [chapter07] 클래스 관계
messycode.tistory.com
01 추상 클래스 알아보기
추상 클래스는 abstract로 지정한 클래스이다.
추상 클래스의 특징은 직접 객체를 생성할 수 없고, 항상 다른 클래스에서 상속해서 추상 메서드를 구현해야 한다.
추상 클래스에서는 추상 속성과 추성 메서드 이외의 일반 속성과 일반 메서드도 정의할 수 있다.
1.1 추상 클래스 정의 규칙
- 추상 클래스는 클래스 앞에 abstract를 붙여야 한다.
- 추상 속성과 메서드를 정의할 때는 abstract예약어를 사용한다.
- 추상 속성과 메서드는 별도의 open지시자를 작성할 필요가 없다.
- 추상 클래스 내에 값이 할당된 속성과 구현된 메세드도 정의할 수 있다.
보통 이런 속성과 메서드를 일반(default) 속성, 일반(default) 메서드라고 한다.
하위 클래스에서 상속하여면 open지시자가 필요하다. - 구현 클래스에서 override한 속성이나 메서드가 하위 클래스에서 상속을 금지하려면 final지시자가 필요하다.
- 추상 클래스는 일반 클래스를 상속받을 수 있다.
일반 클래스가 추상 클래스를 상속받아 구현한 경우 이를 다시 override abstract를 붙여서 추상화시킬 수 있다. - 추상클래스도 주 생성자 정의가 가능하다. 구현 클래스가 생성자 있는 추상 클래스를 상속받아 구현했다면 위임호출 할 때 생성자의 인자를 전달해야 한다.
- 추상 클래스에 아무런 속성이 없어도 구현 클래스에서 상속할 때는 위임 호출이 필요하다.
- 추상 클래스 내부에도 초기화 블록을 정의할 수 있다.
- 추상 클래스 내부에 다른 클래스의 확장 함수를 추상화 메서드로 정의할 수 있다.
- 추상 클래스로 확장함수를 정의할 수 있다. 이 확장함수는 추상클래스를 상속한 클래스에서 모두 사용할 수 있다.
1.2 추상클래스
- 추상 클래스를 정의하면 반드시 구현 클래스에서 상속을 하고 추상 속성과 추상 메서드를 구현해야 사용할 수 있다.
- 일반 메서드를 정의하고, open을 처리해서 상속받는 클래스에서 이 메서드를 재 정의할 수 있다.
- 추상 클래스도 주 생성자 정의가 가능하며, 초기화 블록을 제공한다.
- 추상 클래스 -> 상속받은 클래스 순으로 실행
abstract class Mammal(val birth: String, val name: String) {
init{
println("Mammal 초기화")
}
abstract val specie: String// 추상 프로퍼티
open fun eat() { // 일반 메서드 정의
println("먹는다")
}
abstract fun sleep() // 추상 메서드 정의
}
class Human(birth: String, name: String) : Mammal(birth, name) {
init{
println("Human 초기화")
}
override val specie: String = "Human" // 추상 프로퍼티 구현
override fun sleep() { // 추상 메서드 구현
println("침대에서 잔다")
}
override fun eat() { // 일반 메서드 재정의
println("접시에 담아 먹는다")
}
}
fun main() {
// Mammal 클래스는 추상 클래스이므로 인스턴스화 할 수 없다.
val human = Human("1980-05-30", "김철수")
// Mammal 초기화 출력
// Human 초기화 출력
human.eat() // 일반 메서드 사용 -> "먹는다" 출력
human.sleep() // 추상 메서드 사용 -> "침대에서 잔다" 출력
}
1.3 추상 클래스 활용
추상 클래스를 정의하고 이를 Object 표현식에서 상속받아 익명 객체를 만들 수 있다.
abstract class Weapon {
abstract fun attack()
fun move(){
println("Move")
}
}
fun main() {
val w2 = object : Weapon() { // 객체 표현식으로 익명객체 추상 메서드 구현
override fun attack() {
println("Attack with sword")
}
}
w2.attack() // Attack with sword
w2.move() // Move
}
추상 클래스 내에 추상 확장함수 처리
추상 클래스 안에 다른 클래스의 추상 메서드를 확장 함수로 정의할 수 있다.
abstract class Base {
abstract fun String.extension(x: String): String // 문자열 확장 함수를 추상 메서드로 정의
}
class Derived : Base() {
override fun String.extension(x: String): String { // 추상 메서드를 오버라이딩
return this + x
}
fun callExtension(name: String): String { // 확장 함수를 정의
return "Hello ".extension(name) // 확장 함수를 호출
}
}
fun main() {
val derived = Derived()
println(derived.callExtension("찬영")) // Hello 찬영
}
02 인터페이스 알아보기
코틀린은 단일 상속만 지원한다. 대신 인터페이스를 상속해서 추상 메서드를 구현할 수 있다. 인터페이스도 추상 클래스처럼 계층 구조를 만들 수 있다. 또, 인터페이스는 변수 등에 지정하는 자료형으로 사용할 수 있다.
2.1 클래스와 인터페이스 차이
- 클래스는 객체를 생성해서 내부의 속성과 메서드를 사용한다.
- 인터페이스는 추상 속성과 추상 메서드를 기본으로 처리(인터페이스 내부는 모두 추상화된 것을 기준으로 함)한다. 하지만 일반 속성과 메서드로 구현해서 지원할 수 있다.
인터페이스도 추상 클래스처럼 객체를 생성할 수 없다. - 보통 인터페이스는 클래스에 상속해야 사용되지만, 인터페이스에 확장함수를 작성하면 하위의 모든 클래스의 객체는 이 확장함수를 사용할 수 있다.
- 클래스와 인터페이스는 자료형으로 사용할 수 있어서 변수, 매개변수, 함수 반환 자료형에 자료형으로 지정할 수 있다.
2.2 상속과 구현의 차이
- 상속(inheritance)
- 추상 클래스나 일반 클래스 간의 관계를 구성하는 것
상속시 슈퍼클래스와 서브클래스로 표현
- 추상 클래스나 일반 클래스 간의 관계를 구성하는 것
- 구현(implements)
- 클래스가 인터페이스 내의 추상 멤버를 일반 멤버로 작성하는 것이다.
구현은 인터페이스와 구현 클래스로 표현
- 클래스가 인터페이스 내의 추상 멤버를 일반 멤버로 작성하는 것이다.
2.3 인터페이스 정의 규칙
- interface예약어를 사용
- 모두 추상화된 것이라고 보므로, abstract지시자는 사용할 필요 없음
- 속성과 메서드는 지시자를 별도로 붙이지 않아서 구현 여부로 추상과 일반으로 구분한다.
- 일반 속성을 정의하면 배킹필드가 없어 반드시 게터 메서드에 초깃값을 처리해야 한다.
- 구현 클래스나 인터페이스는
추상클래스와 달리복수의 인터페이스를 상속할 수 있다. - 구현 클래스는 인터페이스에 정의된 추상 속성과 추상 메서드는 반드시 구현해야 한다.
- 구현 클래스는 인터페이스의 일반 속성과 일반 메서드 중에 open지시자가 있으면 재정의 할 수 있다.
- 인터페이스도 하나의 자료형이다. 변수나 매개변수, 반환에 자료형으로 사용할 수 있다.
2.4 인터페이스 정의
- 인터페이스는 기본이 추상이라 별도로 abstract를 표기하지 않는다.
interface Clickable {
fun up()
fun down()
// 인터페이스에 배킹필드가 없어서 반드시 게터 메서드에 초기값 처리 필요
// val abstractProperty: String
// property with implementation
val gauge: Int
get() = 0
}
class TvVolume() : Clickable { // 인터페이스 상속
override var gauge = 10 // 오버라이드
// 만약 gauge 를 ov/erride 하지 않더라도 인터페이스에서 선언한 기본값 0이 들어감
override fun up() {
gauge++
println("Volume up")
}
override fun down() {
gauge--
println("Volume down")
}
}
코틀린의 인터페이스 다중상속
코틀린에서는 클래스끼리 다중상속을 할 수는 없지만, 클래스가 인터페이스를 다중상속받는 것은 가능하다.
// 코틀린에서는 클래스가 여러개의 인터페이스를 다중상속 받는것이 가능하다.
interface A {
fun interfaceFunc() {
println("A")
}
}
interface B {
fun interfaceFunc() {
println("B")
}
}
class C : A, B {
override fun interfaceFunc() {
super<A>.interfaceFunc()
super<B>.interfaceFunc()// super<B>.interfaceFunc()을 하면 B가 출력된다.
}
fun functionC() {
println("C")
}
}
val c = C()
c.interfaceFunc() // A, B
c.functionC()
확장함수
인터페이스도 하나의 자료형이므로 확장함수를 정의할 수 있다.
인터페이스에 확장함수를 정의하면 이 인터페이스를 상속한 모든 하위 클래스에서 사용할 수 있다.
interface Context {
fun absMethod(): String
}
class A : Context {
override fun absMethod(): String = "A"
}
fun Context.extensionMethod(): String = "extension"
fun main() {
val a = A()
println(a.absMethod())
println(a.extensionMethod())
}
03 봉인 클래스 알아보기
봉인 클래스도 추상 클래스이다.
별도로 지정해 사용하는 이유는 특정 추상 클래스를 상속해 구현하는 것을 제한하기 위함이다.
3.1 봉인 클래스 정의 규칙
- 봉인 클래스는 항상 최상위 클래스가 되어야 하므로 가장 먼저 정의한다.
- 봉인 클래스를 상속하는 서브 클래스는 반드시 같은 파일 내에 선언한다.
단, 봉인 클래스를 상속하지 않고 그 서브클래스를 상속한 경우는 같은 파일에 작성하지 않아도 된다. - 봉인 클래스 내부에 서브클래스 정의도 가능하다. (내부 클래스는 private 생성자만 가진다.)
- 봉인 클래스는 기본적으로 abstract클래스 이다.
- 서브클래스는 class, data class, object 모두 가능
- 봉인클래스에 확장 함수를 추가할 수 있다.
sealed class SealedClass // 봉인클래스 정의
class AClass : SealedClass() // 봉인클래스 상속
class BClass : SealedClass()
object CObject : SealedClass()
data class DDataClass(val name: String) : SealedClass()
sealed class SealedClass1{ // 봉인 클래스 정의
class AClass1 : SealedClass() // 클래스 내부에 정의
class BClass1 : SealedClass()
object CObject1 : SealedClass() // object 정의도 상속 가능
data class DDataClass1(val name: String) : SealedClass() // 데이터 클래스도 상속 가능
val a = AClass1()
}
val a = AClass()
val a2 = SealedClass1.DDataClass1("name") // 봉인 클래스 내부 참조
봉인 클래스 내의 속성과 상속 처리
봉인 클래스의 생성자 정의와 봉인 클래스를 상속한 클래스를 다른 클래스가 어떻게 처리하는지 상속 방식의 차이
sealed class A(var name: String) // 봉인 클래스에 생성자 정의
class B : A("B클래스") // 봉인 클래스 상속한 클래스 정의
class C : A("C클래스")
sealed class AA private constructor(var name: String) {
class B : AA("B클래스")// 내부 클래스에서 위임 호출
class C : AA("C클래스")
}
sealed class Fruit() {
class Apple : Fruit()
class Banana : Fruit()
open class Unknown : Fruit() {
fun print() = "Unknown"
}
}
fun main() {
println(B().name) // B클래스
println(AA.B().name)
class Tomato : Fruit.Unknown() // 내부의 클래스도 상속 가능
// 다른 파일인 경우 봉인클래스를 상속한 경우 내부의 클래스를 상속할 수 있다.
println(Tomato().print()) // Unknown
}
sealed class의 사용
- 제한된 계층 모델링: 변형이 가능한 제한된 집합을 가질 수 있는 형식을 모델링 하려는 경우
- 상태머신: 상태머신에서 상태 및 상태 전환을 모델링함
- sealed class의 서브 클래스는 고유한 상태를 가지며, 상태 간에 허용되는 전환을 sealed class 계층으로 정의 할 수 있음
- 결과 형식: 함수형 프로그래밍에서 성공, 실패 또는 사용자 지정 결과와 같은 여러 기능을 가질 수 있는 결과를 나타내는 데 사용됨
- APi응답: api 작업시, 성공 응답, 오류 및 기타 응답 유형
- 이벤트 처리: 이벤트 기반 시스템에서 다양한 유형의 이벤트 또는 작업을 나타냄
sealed class Shape {
class Circle(val radius: Double) : Shape()
class Rectangle(val width: Double, val height: Double) : Shape()
class Triangle(val base: Double, val height: Double) : Shape()
}
서로 다른 모양을 나타내는 세개의 하위 클래스가 있는 sealed class의 활용
fun calculateArea(shape: Shape): Double {
return when (shape) {
is Shape.Circle -> Math.PI * shape.radius * shape.radius
is Shape.Rectangle -> shape.width * shape.height
is Shape.Triangle -> 0.5 * shape.base * shape.height
}
}
아래와 같이 api응답으로도 사용할 수 있다.
sealed class ApiResponse<out T> {
data class Success<out T>(val data: T) : ApiResponse<T>()
data class Error(val message: String) : ApiResponse<Nothing>()
}
// Example usage:
fun fetchDataFromApi(): ApiResponse<String> {
// Simulate a successful API call
val responseData = "Data from the API"
return ApiResponse.Success(responseData)
}
fun main() {
val apiResponse: ApiResponse<String> = fetchDataFromApi()
when (apiResponse) {
is ApiResponse.Success -> {
val data = apiResponse.data
println("API Success: $data")
}
is ApiResponse.Error -> {
val errorMessage = apiResponse.message
println("API Error: $errorMessage")
}
}
}
'코틀린 > 개발자를 위한 코틀린 프로그래밍' 카테고리의 다른 글
[chapter11] 위임(delegation) 확장알아보기 (0) | 2023.09.21 |
---|---|
[chapter10] 함수 추가사항 알아보기 (0) | 2023.09.16 |
[chapter08] 컬렉션 알아보기 (0) | 2023.08.31 |
[chapter07] 클래스 관계 등 추가사항 알아보기 (0) | 2023.08.27 |
[chapter06] 내장 자료형 알아보기 (0) | 2023.08.18 |