개발자/kotlin 코틀린

kotlin 클래스 개념 확실하게 이해하기 2

지구빵집 2022. 1. 21. 09:16
반응형

 

 

서브클래스 만들기 

 

SquareCabin 서브클래스 만들기 

 

1. Dwelling 클래스 아래에서 SquareCabin이라는 클래스를 만듭니다.

 

class SquareCabin

 

2. 다음으로 SquareCabin이 Dwelling과 관련이 있음을 나타내야 합니다. 코드에서 SquareCabin이 Dwelling에서 확장된다고(또는 Dwelling)의 서브클래스라고) 나타내려 합니다. SquareCabin이 Dwelling의 추상 부분에 관한 구현을 제공하기 때문입니다. SquareCabin 클래스 이름 다음에 콜론(:)을 추가하고 상위 Dwelling 클래스를 초기화하는 호출을 추가하여 이 상속 관계를 나타냅니다. Dwelling 클래스 이름 뒤에 괄호를 추가해야 합니다.

 

class SquareCabin : Dwelling()

 

3. 슈퍼클래스에서 확장할 때는 슈퍼클래스에서 예상하는 필수 매개변수를 전달해야 합니다. Dwelling에는 입력으로 residents 수가 필요합니다. 3과 같이 고정된 거주자 수를 전달할 수 있습니다.

 

class SquareCabin : Dwelling(3)

 

그러나 개발자는 프로그램의 유연성을 높여 가변적인 SquareCabins 거주자 수를 허용하기를 바랍니다. 따라서 residents를 SquareCabin 클래스 정의의 매개변수로 만듭니다. residents를 val,로 선언하지 마세요. 이미 상위 클래스 Dwelling에서 선언된 속성을 재사용하고 있기 때문입니다.

 

class SquareCabin(residents: Int) : Dwelling(residents)

 

참고: 이러한 클래스 정의로 내부에서 많은 일이 발생합니다.

 

클래스 헤더에 class SquareCabin(residents: Int) ...가 표시됩니다.

실제로는 class SquareCabin constructor(residents: Int) ...의 약어입니다.

 

constructor는 클래스에서 객체 인스턴스를 만들 때 호출됩니다. 예를 들어 SquareCabin(4)를 호출하면 SquareCabin의 constructor가 호출되어 객체 인스턴스를 초기화합니다.

 

constructor는 전달된 인수를 비롯하여 클래스의 모든 정보에서 인스턴스를 빌드합니다. 클래스가 상위 클래스에서 속성과 함수를 상속받을 때 constructor는 상위 클래스의 constructor를 호출하여 객체 인스턴스 초기화를 완료합니다.

 

따라서 SquareCabin(4)를 사용하여 인스턴스를 만들 때 SquareCabin의 constructor가 실행되고 상속 관계로 인해 Dwelling 생성자도 실행됩니다. Dwelling 클래스 정의는 생성자에 residents 매개변수가 필요하다는 것을 명시합니다. 따라서 SquareCabin 클래스 정의에 Dwelling 생성자에 전달된 residents가 표시됩니다. 생성자에 관한 자세한 내용은 이후 Codelab을 참고하세요.

 

4. 코드를 실행합니다.

 

5. 다음과 같이 오류가 발생합니다.

 

Class 'SquareCabin' is not abstract and does not implement abstract base class member public abstract val buildingMaterial: String defined in Dwelling

 

추상 함수 및 변수를 선언하는 것은 나중에 값과 구현을 제공하겠다는 약속과 다름없습니다. 변수의 경우 추상 클래스의 모든 서브클래스가 값을 제공해야 한다는 의미입니다. 함수의 경우 모든 서브클래스가 함수 본문을 구현해야 한다는 의미입니다.

 

Dwelling 클래스에서 abstract 변수 buildingMaterial을 정의했습니다. SquareCabin은 Dwelling의 서브클래스이므로 buildingMaterial 값을 제공해야 합니다. override 키워드를 사용하여 이 속성이 상위 클래스에서 정의되었고 이 클래스에서 재정의될 거라고 나타냅니다.

 

6. SquareCabin 클래스 내에서 buildingMaterial 속성을 override하고 "Wood" 값을 할당합니다.

 

7. capacity에도 같은 작업을 실행하여 거주민 6명이 SquareCabin에 살 수 있다고 나타냅니다.

 

class SquareCabin(residents: Int) : Dwelling(residents) {
    override val buildingMaterial = "Wood"
    override val capacity = 6
}

 

완성된 코드는 다음과 같이 표시됩니다.

 

abstract class Dwelling(private var residents: Int) {
    abstract val buildingMaterial: String
    abstract val capacity: Int

    fun hasRoom(): Boolean {
       return residents < capacity
   }
}

class SquareCabin(residents: Int) : Dwelling(residents) {
    override val buildingMaterial = "Wood"
    override val capacity = 6
}

 

코드를 테스트하려면 프로그램에서 SquareCabin 인스턴스를 만듭니다.

 

SquareCabin 사용

 

1. Dwelling 및 SquareCabin 클래스 정의 앞에 빈 main() 함수를 삽입합니다.

 

fun main() {

}

abstract class Dwelling(private var residents: Int) {
    ...
}

class SquareCabin(residents: Int) : Dwelling(residents) {
    ...
}

 

2. main() 함수 내에서 거주민이 6명인 squareCabin이라는 SquareCabin 인스턴스를 만듭니다. 건축 자재, 수용 인원, hasRoom() 함수에 관한 print 문을 추가합니다.

 

fun main() {
    val squareCabin = SquareCabin(6)

    println("\nSquare Cabin\n============")
    println("Capacity: ${squareCabin.capacity}")
    println("Material: ${squareCabin.buildingMaterial}")
    println("Has room? ${squareCabin.hasRoom()}")
}

 

hasRoom() 함수는 SquareCabin 클래스에서 정의되지 않았고 Dwelling 클래스에서 정의되었습니다. SquareCabin이 Dwelling 클래스의 서브클래스이므로 hasRoom() 함수는 무료로 상속되었습니다. 이제 hasRoom() 함수를 코드 스니펫에서 squareCabin.hasRoom().으로 표시된 대로 모든 SquareCabin 인스턴스에서 호출할 수 있습니다.

 

3. 코드를 실행하면 다음과 같이 출력됩니다.

 

Square Cabin
============
Capacity: 6
Material: Wood
Has room? false

 

거주민이 6으로, capacity와 같은 squareCabin을 만들었으므로 hasRoom()은 false를 반환합니다. 더 적은 수의 residents로 SquareCabin 초기화를 시도할 수 있습니다. 그런 다음 프로그램을 다시 실행하면 hasRoom()이 true를 반환합니다.

 

with를 사용하여 코드 단순화

 

println() 문에서 squareCabin의 속성이나 함수를 참조할 때마다 squareCabin.을 얼마나 반복해야 하는지 확인합니다. 이 작업은 반복적이며 print 문을 복사하여 붙여넣을 때 오류의 원인이 될 수 있습니다.

 

클래스의 특정 인스턴스로 작업하고 이 인스턴스의 여러 속성과 함수에 액세스해야 한다면 with 문을 사용하여 '이 인스턴스 객체로 다음 작업을 모두 실행'하라고 나타낼 수 있습니다. with 키워드로 시작하고 괄호로 묶인 인스턴스 이름, 실행하려는 작업이 포함된 중괄호가 차례로 이어집니다.

 

with (instanceName) {
    // all operations to do with instanceName
}

 

1. main() 함수에서 with를 사용하도록 print 문을 변경합니다.

 

2. print 문에서 squareCabin.을 삭제합니다.

 

with(squareCabin) {
    println("\nSquare Cabin\n============")
    println("Capacity: ${capacity}")
    println("Material: ${buildingMaterial}")
    println("Has room? ${hasRoom()}")
}

 

3. 다시 코드를 실행하여 오류 없이 실행되고 출력이 동일하게 표시되는지 확인합니다.

 

Square Cabin
============
Capacity: 6
Material: Wood
Has room? false

 

완성된 코드는 다음과 같습니다.

 

fun main() {
    val squareCabin = SquareCabin(6)

    with(squareCabin) {
        println("\nSquare Cabin\n============")
        println("Capacity: ${capacity}")
        println("Material: ${buildingMaterial}")
        println("Has room? ${hasRoom()}")
    }
}

abstract class Dwelling(private var residents: Int) {
    abstract val buildingMaterial: String
    abstract val capacity: Int

    fun hasRoom(): Boolean {
       return residents < capacity
   }
}

class SquareCabin(residents: Int) : Dwelling(residents) {
    override val buildingMaterial = "Wood"
    override val capacity = 6
}

 

RoundHut 서브클래스 만들기

 

1. SquareCabin과 같은 방식으로 또 다른 서브클래스인 RoundHut을 Dwelling에 추가합니다.

 

2. buildingMaterial을 재정의하고 "Straw" 값을 제공합니다.

 

3. capacity를 재정의하고 4로 설정합니다.

 

class RoundHut(residents: Int) : Dwelling(residents) {
    override val buildingMaterial = "Straw"
    override val capacity = 4
}

 

4. main()에서 거주민이 3인 RoundHut 인스턴스를 만듭니다.

 

val roundHut = RoundHut(3)

 

5. 아래 코드를 추가하여 roundHut에 관한 정보를 출력합니다.

 

with(roundHut) {
    println("\nRound Hut\n=========")
    println("Material: ${buildingMaterial}")
    println("Capacity: ${capacity}")
    println("Has room? ${hasRoom()}")
}

 

6. 코드를 실행하면 전체 프로그램이 다음과 같이 출력됩니다.

 

Square Cabin
============
Capacity: 6
Material: Wood
Has room? false

Round Hut
=========
Material: Straw
Capacity: 4
Has room? true

 

이제 다음과 같은 클래스 계층 구조가 있습니다. Dwelling이 루트 클래스, SquareCabin과 RoundHut이 Dwelling의 서브클래스입니다.

 

&amp;nbsp; Dwelling 이 루트 클래스,&amp;nbsp; SquareCabin 과&amp;nbsp; RoundHut 이&amp;nbsp; Dwelling 의 서브클래스

 

RoundTower 서브클래스 만들기

 

이 클래스 계층 구조의 마지막 클래스는 둥근 타워입니다. 둥근 타워를 층이 여러 개인 돌로 만든 둥근 오두막으로 생각하면 됩니다. 따라서 RoundTower를 RoundHut의 서브클래스로 만들 수 있습니다.

 

1. RoundHut의 서브클래스인 RoundTower 클래스를 만듭니다. residents 매개변수를 RoundTower 생성자에 추가하고 이 매개변수를 RoundHut 슈퍼클래스 생성자에 전달합니다.

 

2. buildingMaterial을 "Stone"으로 재정의합니다.

 

3. capacity를 4로 설정합니다.

 

class RoundTower(residents: Int) : RoundHut(residents) {
    override val buildingMaterial = "Stone"
    override val capacity = 4
}

 

1. 이 코드를 실행하면 오류가 발생합니다.

 

This type is final, so it cannot be inherited from

 

이 오류는 RoundHut 클래스를 서브클래스로 분류하거나 상속할 수 없음을 의미합니다. 기본적으로 Kotlin에서 클래스는 최종 클래스이며 서브클래스로 분류할 수 없습니다. abstract 클래스나 open 키워드로 표시된 클래스에서만 상속할 수 있습니다. 따라서 상속될 수 있도록 RoundHut 클래스를 open 키워드로 표시해야 합니다.

 

참고: 추상 클래스를 정의할 때 open 키워드를 사용하지 않아도 됩니다. 예를 들어 Dwelling 클래스를 open 키워드로 표시하지 않아도 됩니다. 추상 속성과 함수를 구현하기 위해 추상 클래스를 서브클래스로 분류하려 한다고 가정합니다.

 

4. RoundHut 선언 시작 부분에 open 키워드를 추가합니다.

 

open class RoundHut(residents: Int) : Dwelling(residents) {
   override val buildingMaterial = "Straw"
   override val capacity = 4
}

 

5. main()에서 roundTower 인스턴스를 만들고 관련 정보를 출력합니다.

 

val roundTower = RoundTower(4)
with(roundTower) {
    println("\nRound Tower\n==========")
    println("Material: ${buildingMaterial}")
    println("Capacity: ${capacity}")
    println("Has room? ${hasRoom()}")
}

 

전체 코드는 다음과 같습니다.

 

fun main() {
    val squareCabin = SquareCabin(6)
    val roundHut = RoundHut(3)
    val roundTower = RoundTower(4)

    with(squareCabin) {
        println("\nSquare Cabin\n============")
        println("Capacity: ${capacity}")
        println("Material: ${buildingMaterial}")
        println("Has room? ${hasRoom()}")
    }

    with(roundHut) {
        println("\nRound Hut\n=========")
        println("Material: ${buildingMaterial}")
        println("Capacity: ${capacity}")
        println("Has room? ${hasRoom()}")
    }

    with(roundTower) {
        println("\nRound Tower\n==========")
        println("Material: ${buildingMaterial}")
        println("Capacity: ${capacity}")
        println("Has room? ${hasRoom()}")
    }
}

abstract class Dwelling(private var residents: Int) {
    abstract val buildingMaterial: String
    abstract val capacity: Int

    fun hasRoom(): Boolean {
       return residents < capacity
   }
}

class SquareCabin(residents: Int) : Dwelling(residents) {
    override val buildingMaterial = "Wood"
    override val capacity = 6
}

open class RoundHut(residents: Int) : Dwelling(residents) {
   override val buildingMaterial = "Straw"
   override val capacity = 4
}

class RoundTower(residents: Int) : RoundHut(residents) {
    override val buildingMaterial = "Stone"
    override val capacity = 4
}

 

6. 코드를 실행합니다. 이제 오류 없이 작동하고 다음과 같이 출력됩니다.

 

Square Cabin
============
Capacity: 6
Material: Wood
Has room? false

Round Hut
=========
Material: Straw
Capacity: 4
Has room? true

Round Tower
==========
Material: Stone
Capacity: 4
Has room? false

 

RoundTower에 여러 층 추가

 

RoundHut은 암묵적으로 단층 건물입니다. 타워에는 보통 여러 층이 있습니다.

 

수용 인원을 고려하면 타워에 층이 많을수록 수용 인원도 많아집니다.

 

여러 층이 있도록 RoundTower를 수정하고 층수에 따라 수용 인원을 조정합니다.

 

1. 층수에 추가 정수 매개변수 val floors를 사용하도록 RoundTower 생성자를 업데이트합니다. residents 뒤에 삽입합니다. 이 매개변수를 상위 RoundHut 생성자에 전달하지 않아도 됩니다. floors가 여기 RoundTower에서 정의되고 RoundHut에는 floors가 없기 때문입니다.

 

class RoundTower(
    residents: Int,
    val floors: Int) : RoundHut(residents) {

    ...
}

 

참고: 매개변수가 여러 개 있고 클래스나 함수 정의가 너무 길어져 한 줄에 여유 있게 맞지 않는 경우 위 코드와 같이 여러 줄로 나눌 수 있습니다.

 

2. 코드를 실행합니다. main() 메서드에서 roundTower를 만들 때 오류가 발생합니다. floors 인수에 숫자를 제공하지 않기 때문입니다. 누락된 인수를 추가할 수 있습니다.

 

또는 RoundTower의 클래스 정의에서 아래와 같이 floors의 기본값을 추가할 수 있습니다. 그런 다음 생성자에 전달되는 floors 값이 없으면 기본값을 사용하여 객체 인스턴스를 만들 수 있습니다.

 

3. 코드에서 floors 선언 다음에 = 2를 추가하여 기본값 2를 할당합니다.

 

class RoundTower(
    residents: Int,
    val floors: Int = 2) : RoundHut(residents) {

    ...
}

 

4. 코드를 실행합니다. 이제 RoundTower(4)가 기본값이 2층인 RoundTower 객체 인스턴스를 만들므로 컴파일됩니다.

 

5. RoundTower 클래스에서 층수를 곱하도록 capacity를 업데이트합니다.

 

override val capacity = 4 * floors

 

6. 코드를 실행하면 이제 2층 RoundTower의 수용 인원이 8입니다.

 

완성된 코드는 다음과 같습니다.

 

fun main() {

    val squareCabin = SquareCabin(6)
    val roundHut = RoundHut(3)
    val roundTower = RoundTower(4)

    with(squareCabin) {
        println("\nSquare Cabin\n============")
        println("Capacity: ${capacity}")
        println("Material: ${buildingMaterial}")
        println("Has room? ${hasRoom()}")
    }

    with(roundHut) {
        println("\nRound Hut\n=========")
        println("Material: ${buildingMaterial}")
        println("Capacity: ${capacity}")
        println("Has room? ${hasRoom()}")
    }

    with(roundTower) {
        println("\nRound Tower\n==========")
        println("Material: ${buildingMaterial}")
        println("Capacity: ${capacity}")
        println("Has room? ${hasRoom()}")
    }
}

abstract class Dwelling(private var residents: Int) {
    abstract val buildingMaterial: String
    abstract val capacity: Int

    fun hasRoom(): Boolean {
       return residents < capacity
   }
}

class SquareCabin(residents: Int) : Dwelling(residents) {
    override val buildingMaterial = "Wood"
    override val capacity = 6
}

open class RoundHut(residents: Int) : Dwelling(residents) {
    override val buildingMaterial = "Straw"
    override val capacity = 4
}

class RoundTower(
    residents: Int,
    val floors: Int = 2) : RoundHut(residents) {

    override val buildingMaterial = "Stone"
    override val capacity = 4 * floors
}

 

여기까지 잘 하셨습니다. 길어지는 이유로 마지막 포스팅과 전체 코드는 다음 포스팅 kotlin 클래스 개념 확실하게 이해하기 3 으로 넘어갑니다. ^^ 

 

 

kotlin 클래스 개념 확실하게 이해하기 2

 

 

 

반응형