개요
안드로이드 프로그래밍할때, 우리는 굉장히 많은 메소드를 오버라이딩합니다. 그런데 오버라이딩 시 super 문법을 통해 부모 클래스의 메소드를 호출하는 것 super.onSaveInstanceState(outState);
을 매우 자주 보았을 겁니다. 왜 사용할까요?
메소드 오버라이딩은 일반적으로 부모 클래스 메소드를 사용하지 않고 재정의한 메소드를 사용하기 위함인데, 굳이 부모 클래스의 메소드를 호출하면 오버라이딩한 의미가 별로 없지 않나요?
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
...
}
super
먼저 이 상황을 이해하기 위해선 super 문법을 알고 있어야 합니다. super 문법을 통해 부모 클래스의 메소드를 호출할 수 있습니다. super에 대한 자세한 내용은 이 글을 참고하세요.
작성할 필요가 없는 경우
앞서 언급한 onSaveInstanceState
메소드를 오버라이딩할 경우 super.onSaveInstanceState(outState);
를 작성할 필요가 없습니다.
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
...
}
오버라이딩되는 대상, 부모 클래스의 메소드가 아래와 같이 빈 블록으로 정의되어 있기 때문입니다.
@MainThread
public void onSaveInstanceState(@NonNull Bundle outState) {
}
빈 블록이 아니더라도, 작성할 필요가 없는 메소드도 있습니다. 아래는 onCreateView 메소드의 정의입니다. null을 반환할 수 있거나, 단순히 멤버 선언일 경우 등등.. 경우에 따라 다양한 이유로 작성할 필요가 없습니다.
@MainThread
@Nullable
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
if (mContentLayoutId != 0) {
return inflater.inflate(mContentLayoutId, container, false);
}
return null;
}
반드시 작성해야 하는 경우
단, 그렇다고 해서 모든 메소드 오버라이딩 시 부모 클래스 메소드를 호출하지 않아도 되는 것은 아닙니다. 아래와 같이 부모 클래스의 메소드, onCreate 메소드는 오버라이딩 시 반드시 부모 클래스의 메소드를 호출해야 합니다.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // 반드시 작성해야 합니다. 미작성 시 에러
...
}
아래는 부모 클래스의 onCreate 메소드 정의입니다. 부모 클래스의 메소드는 또 다시 조부모 클래스의 메소드를 상속받고 조부모 메소드 호출 후 fragment 생명주기를 통제하고 있습니다. onCreate 메소드의 경우 생명주기 통제가 반드시 필요합니다.
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
mFragments.dispatchCreate();
}
따라서 onCreate 메소드는 반드시 오버라이딩 시 부모 메소드를 호출해 주어야 합니다.
작성하면 안되는 경우
onPressedBack 메소드의 경우 상황에 따라 super.onPressedBack(); 을 작성하면 안됩니다. 아래 코드를 보세요.
@Override
public void onBackPressed() {
if(System.currentTimeMillis() - backPressedTime >= 2000) {
Toast.makeText(getBaseContext(), "Press back again to exit", Toast.LENGTH_SHORT).show();
} else {
super.onBackPressed();
}
backPressedTime = System.currentTimeMillis();
}
만약 super.onBackPressed(); 를 블록 제일 앞에 작성한 경우 뒤로가기 버튼은 누르는 경우 무조건 바로 해당 액티비티는 종료됩니다. 즉, 구현하고자 하는 바에 따라 작성하면 안될 수도 있습니다.
@Override
public void onBackPressed() {
super.onBackPressed(); // 부모 클래스의 메소드는 해당 액티비티를 종료시키므로 아래 코드는 의미가 없어집니다.
if(System.currentTimeMillis() - backPressedTime >= 2000) {
Toast.makeText(getBaseContext(), "Press back again to exit", Toast.LENGTH_SHORT).show();
} else {
super.onBackPressed();
}
backPressedTime = System.currentTimeMillis();
}
DRY (Don't repeat yourself)
소프트웨어 공학 이론 중 하나인 DRY는 코드의 중복을 최소화하고 재사용성을 높이기 위한 원칙입니다. 가령, 안드로이드 개발 환경에서 onSaveInstanceState 메소드에 몇 가지 새로운 기능이 추가되었다고 가정해봅시다. 이런 경우, 이미 작성된 코드에 영향을 최소화하면서 새로운 변화에 대응할 수 있어야 합니다.
여기서 super.onSaveInstanceState(outState); 코드가 주목받습니다. onSaveInstanceState 메소드의 변경에 상관없이, 이 코드를 사용하면 앱 개발자는 추가 코드 수정 없이도 새로운 버전에 유연하게 대응할 수 있습니다.
간단히 말하면, super.onSaveInstanceState(outState); 코드는 DRY한 코드를 유지하도록 도와줍니다. 이미 존재하는 코드에 영향을 최소화하면서 새로운 변화에 대응함으로써 개발자는 코드 간 상호 영향을 최소화하고 더욱 유연하게 개발할 수 있습니다.
개인적인 생각
사용하지 말아야 하는 경우를 제외하고, 기본적으로 이 코드를 작성하는 것이 좋다고 생각합니다. 앞서 언급한 DRY 원칙을 지키는 것이 좋기 때문입니다.
참고 자료
https://www.geeksforgeeks.org/how-to-implement-press-back-again-to-exit-in-android/