본문 바로가기
Android & Kotlin/Kotlin

[Kotlin] Inner, nested class 내부 중첩 클래스(+ Recyclerview 리사이블러뷰)

by 말린밴댕이_공부 2024. 5. 3.
반응형

코틀린은 기본적으로 중첩 클래스 (자바는 Inner)

코틀린의 중첨클래스는 명시적으로 요청하지 않는 한 outer Class Insance에 대한 접근 권한이 없다는 점.

 

 

View요소를 하나 만들때 View의 상태를 직렬화 하기 위해 State 인터페이스를 선언하고 Serializable을 구현하며, View 인터페이스안에는 뷰의 상태를 가저와 저장할 때 사용할 getCurrentState 와 restoreState메서드 선언이 있다.

interface State : Serializable

interface View {
    fun getCurrentState() : State
    fun restoreState(state : State) {}
}

우리가 자바에서는 클래스 안에 정의한 클래스는 자동적으로 내부 클래스가 된다.

 

 

public class Button implements View{
	public class ButtonState implements State {}
}

ButtonState는 Button클래스에 대한 참조를 묵시적으로 포함하며 참조로 인해 ButtonState를 직렬화 할 수 없다.

 

이를 자바에서는 중첩 클래스를 static으로 선언하면 클래스를 둘러싼 outer class에 대한 묵시적인 참조가 사라진다.

 

class Button : View {
    override fun getCurrentState(): State = ButtonState()

    override fun restoreState(state: State) { super.restoreState(state) }
    
    class ButtonState : State {}
}

코틀린 중첩 클래스에 아무런 변경자가 붙지 않으면 자바 static 중첩 클래스와 같으며 outer class에 대한 참조를 inner class가 하게 만들고 싶다면 inner 변경자를 붙여야한다.

 

 

클래스 B안에 정의된 클래스 A JAVA Kotlin
중첩 클래스(nested class) statlc class A class A
내부 클래스(inner class) class A inner class A

 

 

안드로이드에서의 리사이클러뷰 inner, nested class

 

안드로이드를 하게 된다면 리사이클러뷰를 만들 때 viewHolder를 inner class로 만든 모습을 본적이 있을 것이다. (처음 안드로이드를 하던 시절 인터넷 보고 따라치면서 학습을 해서 그런것도 있지 않을까?)

 

리사이클러뷰 viewHolder inner class

class ChattingRoomAdapter(var chatList : List<ChatRoomResult>, private val clickListener: chatRoomClickListener<ChatRoomResult>)
    : RecyclerView.Adapter<ChattingRoomAdapter.ChatViewHolder>() {

    // 어댑터 메소드

    inner class ChatViewHolder(val binding : ChattingRoomsListBinding) : RecyclerView.ViewHolder(binding.root){
        fun bind(chattingRoom : ChatRoomResult){
			        //~~
        }
    }
}

앞서 얘기 했듯이 Inner class는 외부 클래스의 인스턴스에 대한 묵시적 참조를 가지고 있다.

따라서 inner class인 뷰홀더가 외부 클래스 맴버인 어댑터를 직접적으로 접근을 할 수 있다는 것이다.

 

 

하지만 inner class를 사용하면서 문제가 없었던 이유는 뭘까?

액티비티 OR 프래그먼트가 Destroy될때 Adapter도 사라지게 된다는 것이다.

 

 

하지만

RecyclerView에서 ViewHolder에서 Inner class를 사용하는 것이 메모리 누수를 발생 시킬수도 있다.

어댑터에 대한 암묵적인 참조를 유지하기 때문에, ViewHolder가 뷰 계층에서 제거되더라고 ViewHolder와 Inner class 간 참조로 인해 유지 될 우려가 있다.

이러한 이유로 nested class를 쓰는 것을 근거 있는 코드를 작성해야한다고 생각한다.

 

class MyRestaurantAdapter(
) : ListAdapter<UiMyListData, MyRestaurantViewHolder>(diffCallback) {

	//Adapter 코드, diffCallback ~~
		
		
    class MyRestaurantViewHolder(
        private val binding: ItemMyListBinding
    ) : RecyclerView.ViewHolder(binding.root) {
    }

}

 

이러한 위험한 코드를 붙이면서까지 inner로 사용한 이유를 증명할 수 없다. 생명주기 덕분에 viewholder가 소멸이 된다해도 outer class로 만들 이유가 없는 의미없는 코드 하지말기!

 

 

결론 

- inner class를 쓸때는 항상 근거있는 경우를 생각하자. 

반응형

댓글