개발자/kotlin 코틀린

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

지구빵집 2022. 1. 20. 09:53
반응형

 

객체, 인스턴스, 상속, 캡슐화, 은닉 등의 용어가 나오는 고수준의 프로그래밍 언어에서 클래스는 많은 기능을 제공해서 그런지 배우기도 어렵다. 개념을 잘 이해하고 있다가도 한참 지나면 또 헤메게 된다. Kotlin 언어를 배우며 다시 클래스에 대한 개념을 공부한다. 아래 내용을 적어도 세 번은 반복한다. 반복해서 타이핑하고 이해하고 똑같이 실습한다. 그러면 개념을 잡을 수 있다. 손가락을 움직이고 몸을 움직여 카피하고 붙여넣고 실제로 해봐야 한다. 무엇인가 익히는 것에 다른 방법은 없다. 

 

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

 

어렵지만 꼭 알아야 하는 어떤 내용을 이해하는 방법은 익숙할 때까지 반복하는 것이다. 용어, 단어, 개념 설명을 분명히 이해할 때까지 반복한다. 꽤 쉽고 잘된 설명을 보아서 연습한다. 출처 링크. 좀 긴 포스팅이지만 처음부터 잘 따라 하면서 이해하면 아주 명확히 클래스 개념에 대해 이해할 수 있다. 클래스는 비단 여기서 뿐만이 아니라 모든 언어에서 중요한 개념이다. 캡슐화, 상속 등 어려운 개념까지 한 번에 이해하길 바란다. 언어는 kotlin으로 실습한다. 아래 실습 환경이 나온다. 웹에서 실행하면 되니 간단하다.

 

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

 

클래스 계층 구조란 무엇인가요? 

 

사람들은 자연스럽게 속성과 동작이 비슷한 항목을 그룹으로 분류하고 그룹 내에서도 일정 유형의 계층 구조를 만듭니다. 예를 들어 채소와 같이 광범위한 카테고리를 사용할 수 있고 그 카테고리 안에 콩류라는 좀 더 구체적인 유형의 카테고리를 사용할 수 있습니다. 콩류 내에서도 더 구체적으로 완두콩, 강낭콩, 렌즈콩, 병아리콩, 대두 등이 있을 수 있습니다.

 

이는 계층 구조로 표현될 수 있는데 콩류는 채소의 속성(예: 식물, 식용 가능함)을 모두 포함하거나 상속받기 때문입니다. 마찬가지로 완두콩, 강낭콩, 렌즈콩도 모두 콩류의 속성과 고유한 자체 속성이 있습니다.

 

이러한 관계를 프로그래밍 측면에서 어떻게 표현할지 살펴보겠습니다. Vegetable을 Kotlin의 클래스로 만들면 Legume을 Vegetable 클래스의 하위 클래스 또는 서브클래스로 만들 수 있습니다. 즉, Vegetable 클래스의 모든 속성과 메서드가 Legume 클래스에 상속(즉, 사용 가능함)됩니다.

 

아래와 같이 클래스 계층 구조 다이어그램으로 이를 표현할 수 있습니다. Vegetable을 Legume 클래스의 상위 클래스 또는 슈퍼클래스로 지칭할 수 있습니다. 

 

Vegetable을 클래스의 상위 클래스 또는 슈퍼클래스로 지칭

 

Legume의 서브클래스(예: Lentil, Chickpea)를 만들어 클래스 계층 구조를 유지하고 확장할 수 있습니다. 그러면 Legume은 Vegetable의 하위 클래스 또는 서브클래스가 될 뿐 아니라 Lentil과 Chickpea의 상위 클래스 또는 슈퍼클래스가 됩니다. Vegetable은 이 계층 구조의 루트 또는 *최상위(*또는 기본) 클래스입니다. 

 

Vegetable은 이 계층 구조의 루트 또는 *최상위(*또는 기본) 클래스

참고: 용어 요약

 

다음은 이 Codelab에서 사용되는 단어와 Kotlin 클래스의 컨텍스트에서 이 단어가 의미하는 내용을 요약한 것입니다.

 

  • 클래스 계층 구조. 클래스가 상위 요소와 하위 요소의 계층 구조로 구성된 배열입니다. 계층 구조 다이어그램은 일반적으로 상위 요소가 하위 요소 위에 표시된 상태로 그려집니다.
  • 하위 클래스 또는 서브클래스. 계층 구조에서 다른 클래스 아래에 있는 모든 클래스입니다.
  • 상위 클래스 또는 슈퍼클래스 또는 기본 클래스. 하위 클래스가 하나 이상 있는 모든 클래스입니다.
  • 루트 또는 최상위 클래스. 클래스 계층 구조의 최상위(또는 루트)에 있는 클래스입니다.
  • 상속. 하위 클래스가 상위 클래스의 모든 속성과 메서드를 포함하거나 상속받는 경우입니다. 이를 통해 코드를 공유하고 재사용할 수 있어 프로그램을 더 쉽게 파악하고 유지할 수 있습니다. 

 

Android 클래스의 상속

 

이전 Codelab에서 한 것처럼 클래스를 사용하지 않고도 Kotlin 코드를 작성할 수 있지만 Android의 여러 부분이 활동, 뷰, 뷰 그룹 등 클래스 형태로 제공됩니다. 따라서 클래스 계층 구조 이해는 Android 앱 개발에 중요하며 이를 통해 Android 프레임워크에서 제공하는 기능을 활용할 수 있습니다.

 

예를 들어 Android에는 화면의 직사각형 영역을 나타내고 그리기와 이벤트 처리를 담당하는 View 클래스가 있습니다. TextView 클래스는 View 클래스의 서브클래스입니다. 즉, TextView는 View 클래스의 모든 속성과 기능을 상속받고 사용자에게 텍스트를 표시하는 특정 로직을 추가합니다. 

 

TextView 클래스는 View 클래스의 서브클래스

 

한 단계 더 나아가 EditText 및 Button 클래스는 TextView 클래스의 하위 클래스입니다. TextView 및 View 클래스의 모든 속성과 메서드를 상속받고 고유한 특정 로직을 추가합니다. 예를 들어 EditText는 화면에서 텍스트를 수정할 수 있는 자체 기능을 추가합니다.

 

View 및 TextView 클래스의 모든 로직을 복사하여 EditText 클래스에 붙여 넣지 않고도 EditText를 View 클래스의 서브클래스인 TextView 클래스의 서브클래스로 분류하면 됩니다. 그러면 EditText 클래스의 코드는 이 UI 구성요소를 다른 뷰와 다르게 하는 요소에 특히 중점을 둘 수 있습니다.

 

developer.android.com 웹사이트의 Android 클래스 문서 페이지 상단에서 클래스 계층 구조 다이어그램을 확인할 수 있습니다. 계층 구조 상단에 kotlin.Any가 표시된다면 그 이유는 Kotlin의 모든 클래스에 공통 슈퍼클래스 Any가 있기 때문입니다. 자세한 내용은 여기를 참고하세요. 

 

Kotlin의 모든 클래스에 공통 슈퍼클래스 Any

 

이와 같이 클래스 간 상속을 활용하는 방법을 학습하면 코드를 더 쉽게 작성하고 재사용하며 읽고 테스트할 수 있습니다. 

 

 

기본 클래스 만들기

 

주택의 클래스 계층 구조

 

이 Codelab에서는 바닥 면적, 층, 거주자가 있는 주택(사람이 사는 집)을 예로 사용하여 클래스 계층 구조의 작동 원리를 보여주는 Kotlin 프로그램을 빌드합니다.

 

다음은 빌드할 클래스 계층 구조의 다이어그램입니다. 루트에는 청사진과 유사하게 모든 주택에 적용되는 속성과 기능을 지정하는 Dwelling이 있습니다. 그런 다음 정사각형 통나무집(SquareCabin) 클래스, 둥근 오두막(RoundHut) 클래스, 여러 층이 있는 RoundHut인 둥근 타워(RoundTower) 클래스가 있습니다.

 

클래스 계층 구조의 다이어그램

 

구현할 클래스는 다음과 같습니다.

 

  • Dwelling: 모든 주택에 공통으로 적용되는 정보를 담고 있는 구체적이지 않은 집을 나타내는 기본 클래스입니다.
  • SquareCabin: 바닥 면적이 정사각형인 나무로 만든 정사각형 통나무집입니다.
  • RoundHut: 바닥 면적이 원형인 짚으로 만든 둥근 오두막이고 RoundTower의 상위 요소입니다.
  • RoundTower: 바닥 면적이 원형이고 층이 여러 개인 돌로 만든 둥근 타워입니다.

 

추상 주택 클래스 만들기

 

어떤 클래스라도 클래스 계층 구조의 기본 클래스나 다른 클래스의 상위 클래스가 될 수 있습니다.

 

'추상' 클래스는 완전히 구현되지 않아서 인스턴스화할 수 없는 클래스입니다. 스케치라고 생각하면 됩니다. 스케치를 통해 무언가에 관한 아이디어와 계획을 통합하지만 그 무언가를 빌드하기에는 일반적으로 정보가 충분하지 않습니다. 스케치(추상 클래스)를 사용하여 청사진(클래스)을 만들고 청사진을 통해 실제 객체 인스턴스를 빌드합니다.

 

일반적으로 슈퍼클래스를 만들어 좋은 점은 모든 서브클래스에 공통적인 속성과 함수를 포함한다는 것입니다. 속성값과 함수 구현을 알 수 없으면 클래스를 추상으로 만듭니다. 예를 들어 Vegetables에는 모든 채소에 공통으로 적용되는 여러 속성이 있지만 구체적이지 않은 채소의 인스턴스를 만들 수는 없습니다. 모양이나 색상 등을 모르기 때문입니다. 따라서 Vegetable은 각 채소에 관한 구체적인 세부정보의 결정을 서브클래스에 맡기는 추상 클래스입니다.

 

추상 클래스 선언은 abstract 키워드로 시작합니다.

 

*참고: 이 과정의 향후 Codelab에서 서브클래스로 분류해야 하는 Android의 여러 추상 클래스를 경험하게 됩니다.

 

Dwelling은 Vegetable과 같은 추상 클래스가 됩니다. 여러 유형의 주택에 공통으로 적용되는 속성과 함수를 포함하지만 속성의 정확한 값과 함수 구현의 세부정보는 알 수 없습니다.

 

1. Kotlin 플레이그라운드(https://developer.android.com/training/kotlinplayground)로 이동합니다.

 

2. 편집기에서 main() 함수 내의 println("Hello, world!")를 삭제합니다.

 

3. 그런 다음 이 코드를 main() 함수 아래에 추가하여 Dwelling이라는 abstract 클래스를 만듭니다.

 

abstract class Dwelling(){
}

 

건축 자재 속성 추가

 

이 Dwelling 클래스에서 주택마다 다를 수 있더라도 모든 주택에 적용되는 항목을 정의합니다. 모든 주택은 건축 자재로 만들어집니다.

 

4. Dwelling 내에서 건축 자재를 나타내는 String 유형의 buildingMaterial 변수를 만듭니다. 건축 자재는 변경되지 않으므로 val을 사용하여 변경 불가능한 변수로 만듭니다.

 

val buildingMaterial: String

 

5. 프로그램을 실행하면 다음 오류가 발생합니다.

 

Property must be initialized or be abstract

 

buildingMaterial 속성에 값이 없습니다. 사실 개발자가 값을 제공할 수 없습니다. 건물이 구체적이지 않아서 구체적인 자재로 만들 수 없기 때문입니다. 따라서 오류 메시지가 나타내듯이 abstract 키워드를 buildingMaterial 선언의 접두사로 지정하여 여기서 정의되지 않음을 나타낼 수 있습니다.

 

6. 변수 정의에 abstract 키워드를 추가합니다.

 

abstract val buildingMaterial: String

 

7. 코드를 실행하면 어떠한 작업도 실행되지 않지만 이제 오류 없이 컴파일됩니다.

 

8. main() 함수에서 Dwelling 인스턴스를 만들고 코드를 실행합니다.

 

val dwelling = Dwelling()

 

9. 추상 Dwelling 클래스의 인스턴스를 만들 수 없으므로 오류가 발생합니다.

 

Cannot create an instance of an abstract class

 

10. 잘못된 코드를 삭제합니다. 지금까지 완료된 코드는 다음과 같습니다.

 

abstract class Dwelling(){
    abstract val buildingMaterial: String
}

 

수용 인원 속성 추가

 

주택의 또 다른 속성은 수용 인원입니다. 즉, 주택에서 살 수 있는 사람 수입니다.

 

모든 주택에는 변경되지 않는 수용 인원이 있습니다. 그러나 수용 인원은 Dwelling 슈퍼클래스 내에서 설정할 수 없습니다. 특정 유형의 주택에 관한 서브클래스에서 정의해야 합니다.

 

1. Dwelling에서 capacity라는 abstract 정수 val을 추가합니다.

 

abstract val capacity: Int

 

거주자 수에 관한 비공개 속성 추가

 

모든 주택에는 주택에 거주하는 여러 residents(capacity 이하일 수 있음)가 있으므로 모든 서브클래스가 상속받아 사용하도록 Dwelling 슈퍼클래스에서 residents 속성을 정의합니다.

 

1. residents를 Dwelling 클래스의 생성자에 전달되는 매개변수로 만들 수 있습니다. residents 속성은 var입니다. 인스턴스가 만들어진 후 거주자 수가 변경될 수 있기 때문입니다.

 

abstract class Dwelling(private var residents: Int) {

 

residents 속성은 private 키워드로 표시됩니다. 비공개는 Kotlin의 공개 상태 수정자로, residents 속성이 이 클래스에만 표시되고 이 클래스 내부에서 사용할 수 있다는 의미입니다. 프로그램의 다른 위치에서는 액세스할 수 없습니다. 속성이나 메서드를 비공개 키워드로 표시할 수 있습니다. 공개 상태 수정자가 지정되지 않은 다른 경우에는 속성과 메서드가 기본적으로 public이고 프로그램의 다른 위치에서 액세스할 수 있습니다. 건축 자재나 건물의 수용 인원에 관한 정보와 비교할 때 주택에 사는 사람 수는 일반적으로 비공개 정보이므로 이러한 판단은 합리적입니다.

 

주택의 capacity와 현재 residents 수가 모두 정의된 상태에서 주택의 또 다른 거주자를 위한 공간이 있는지 확인하는 hasRoom() 함수를 만들 수 있습니다. Dwelling 클래스에서 hasRoom() 함수를 정의하고 구현하면 됩니다. 공간이 있는지 계산하는 공식이 모든 주택에 동일하기 때문입니다. residents 수가 capacity보다 적으면 Dwelling에 공간이 있고 함수는 이 비교에 기반하여 true나 false를 반환해야 합니다.

 

2. hasRoom() 함수를 Dwelling 클래스에 추가합니다.

 

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

 

이 코드를 실행하면 오류가 발생하지 않습니다. 아직 눈에 띄는 작업이 실행되지 않습니다.

 

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

 

abstract class Dwelling(private var residents: Int) {

   abstract val buildingMaterial: String
   abstract val capacity: Int

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

 

여기까지 하고 다음은 서브클래스 만들기는 다음 포스팅으로 넘어갑니다. 너무 길어지니까. 이어지는 포스팅은 kotlin 클래스 개념 확실하게 이해하기 2 입니다. 

 

 

참고

코틀린 강의 영상 - 안드로이드 코틀린 기초 강좌

 

도서관 바다가 보이는 모니터

 

 

 

반응형