본문 바로가기
개발 관련 공부/코틀린

Unit2-1

by 슴새 2022. 9. 13.
반응형
이 포스트는 2021.12~2022.09 기간동안 벨로그에 작성한 글을 티스토리에 옮겨 적은 것입니다.

클래스 계층 구조를 이해하는 것은 Android 앱 개발에 중요하다.

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

위와 같은 계층 구조를 가지는 클래스들을 구현해보자!

abstract class Dwelling(private var residents: Int) {

   abstract val buildingMaterial: String
   abstract val capacity: Int

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

먼저 Dwelling 클래스를 구현하자.
추상 클래스는 완전히 구현되지 않아서 인스턴스화할 수 없는 클래스다.

아직 어떤 자재로 만들지 결정되지 않았으므로 buildingMaterial을 초기화 할 수 없다. abstract으로 선언한다. capacity 변수도 마찬가지.

hasRoom은 방이 남아있는지 여부를 반환하는 함수이다. 고정된 거주자 수는 추상 클래스가 private으로 받는다.

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

Dwelling 클래스 아래에서 SquareCabin이라는 클래스를 만들자. 뒤에 :Dwelling(residents)을 붙여서 상속받은걸 표시.

residents은 이미 Dwelling에서 val로 선언한 걸 재사용하는 것이므로 여기서는 val로 선언할 필요 없다.

Dwelling에서 abstract으로 선언한 buildingMaterial과 capacity를 명시해줘야 한다. override 키워드를 사용하여 이 속성이 상위 클래스에서 정의되었고 이 클래스에서 재정의될 거라고 나타낸다.

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

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

메인 함수에서의 사용 예제이다. with 구문을 사용하면 println("Capacity: ${squareCabin.capacity}") 이렇게 번거롭게 쓰지 않아도 된다.

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
}

RoundHut 클래스 앞에는 open 키워드를 붙여야 한다. abstract 클래스나 open 키워드로 표시된 클래스에서만 상속할 수 있기 때문이다. RoundHut는 아래에 RoundTower 클래스를 두기 위해 open 키워드를 붙여야 한다.

abstract class Dwelling(private var residents: Int) {

   abstract val buildingMaterial: String
   abstract val capacity: Int

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

추상 클래스에 추상 함수를 선언할 수 있다.
이제 Dwelling의 모든 서브 클래스에서는

override fun floorArea(): Double {
	//코드..
}

를 구현해야 한다.

import kotlin.math.PI
open class RoundHut(val residents: Int,
    val radius: Double) : Dwelling(residents) {

    override val buildingMaterial = "Straw"
    override val capacity = 4

    override fun floorArea(): Double {
        return PI * radius * radius
    }
}
val roundHut = RoundHut(3, 10.0)

생성자를 업데이트하여 radius를 받도록 하자. 바닥 면적 계산에 필요한 파이 값은 math 라이브러리에서 임포트해 가져오자. (SquareCabin은 원이 사각형으로 바뀐것만 빼면 RoundHut와 비슷하므로 패스.. )

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

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

    override fun floorArea(): Double {
        return super.floorArea() * floors
    }
}

RoundHut의 서브 클래스인 RoundTower은 super 키워드를 사용하여 상위 클래스의 함수(PI * radius * radius)를 사용할 수 있다. 얘도 원형 바닥의 면적을 구해야 하는 건 똑같으므로!


XML(확장성 마크업 언어)은 텍스트를 구성하는 방법이며 태그, 요소, 속성으로 구성된다. XML을 사용하여 Android 앱의 레이아웃을 정의할 수 있다.

view들은 findViewById()을 통해 접근할 수 있지만... UI가 더 복잡해짐에 따라 findViewById()를 사용하는 것이 번거로워질 수 있다.

이런 단점을 극복하기 위해 Android는 뷰 결합이라는 기능을 제공한다.

buildFeatures {
    viewBinding = true
}

build.gradle(app)의 안드로이드 섹션에 위 코드를 추가하고 sync해주자.

class MainActivity : AppCompatActivity() {

    lateinit var binding: ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
    }
}

MainActivity.kt에서 setContentView 부분을 수정하자.


findViewByID와 View Binding의 차이는 위 그림과 같다.
앱의 각 View마다 findViewById()를 호출하는 대신, 결합 객체를 한 번 만들고 초기화한다.

MainActivity.kt 코드를 살펴보면서 공부해보자.

lateinit var binding: ActivityMainBinding

이 부분이 결합 객체의 최상위 변수를 선언하는 부분이다.
lateinit는 코드가 변수를 사용하기 전에 먼저 초기화할 것임을 확인해 주는 키워드이다.

binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)

이후 onCreate()안에서 binding변수를 초기화해주고, 컨텐츠 뷰를 설정한다.

// findViewById() 사용하는 방법
val myButton: Button = findViewById(R.id.my_button)
myButton.text = "A button"

//view binding을 사용한 혁신적인 방법!
val myButton: Button = binding.myButton
myButton.text = "A button"

이제 findViwById 없이 view를 찾을 수 있다.

참고자료:https://developer.android.com/courses/pathways/android-basics-kotlin-unit-2-pathway-1

 
반응형

'개발 관련 공부 > 코틀린' 카테고리의 다른 글

Unit3-1(1)  (0) 2022.09.13
Unit2-3  (1) 2022.09.13
Unit2-2  (0) 2022.09.13
Unit1-3 & Unit1-4  (1) 2022.09.13
Unit1-1 & Unit1-2  (0) 2022.09.13

댓글