클래스 내의 메서드들 중 비슷한 일을 하는 부분이 있는 경우 중복 코드를 제거하기 위해
private
helper method로 추출하곤 한다. 이런 상황에서 굳이static
키워드를 붙이는 것이 무슨 의미가 있을까? 궁금해져 정리해본다.
private static
method의 의미
- private : 해당 클래스 내에서만 접근 가능함
- static : 해당 클래스의 인스턴스를 통하지 않고서도 접근 가능함
클래스의 인스턴스 필드를 참조하지 않는 경우 이 메서드는 static
으로도 정의 가능하다. 어차피 외부의 다른 클래스에게 어떠한 기능을 제공할 목적이 아니므로 private
접근 제어자가 붙는 것은 합리적이지만, 그에 비해 method는 런타임 메모리 레이아웃에서 메서드 공간에 바이트 코드가 로드 되어 모든 인스턴스 별로 공유하기 때문에 어차피 인스턴스 별로 메모리 공간을 필요로 하지도 않는다.
그렇다면 굳이 객체의 상태를 참조하지 않는 private method
를 static
으로 까지 정의할 필요가 있을까?
static : 상태에 대한 비의존성을 명시
이 메서드는 객체(인스턴스)의 상태를 변경하지 않음
을 명시하는 것이다. 즉, 이 메서드는 객체의 상태, 그리고 이 객체와 의존성이 없음을 명확하게 공유 하는 것에 의미가 있다. 향 후 이 메서드를 수정 하려는 모든 이는 이러한 설계 의도를 알게 된다.
또한 특정 객체에 의존성이 없기 때문에 별도의 클래스로 분리하는 것을 고려해볼 수 있다. 즉, 해당 클래스의 static field
를 참조하지 않는 이상은 꼭 해당 클래스에 이 메서드가 존재 해야 하는지에 대한 리팩토링의 근거가 될 수도 있다.
static, private : final
method임을 암시
private
메서드와 static
메서드는 암묵적으로 final
메서드임을 의미한다는 것이다.
final
메서드는 해당 메서드가 존재하는 클래스를 상속 받은 클래스가 존재 하더라도 그 클래스에서의 메서드 오버라이딩을 허용하지 않는다. 즉 이 메서드의 구현은 이 클래스의 구현이 마지막인 것이다. 해당 클래스가 아니더라도 접근 가능할 수 있으나 오버라이딩은 허용 하지 않고자 하는 경우다.
private
메서드는 다른 클래스에서 접근을 허용하지 않기 위해 사용한다. 그렇기 때문에 원천적으로 오버라이딩 자체가 불가능 하기도 하다.
static
메서드 또한 오버라이딩을 허용하지 않는다. 아래 경우 처럼 @Override
어노테이션을 명시하면 컴파일 에러가 발생한다.
1 |
|
hiding
기법(@Override
제거)을 사용하면 메서드 자체를 구현할 수는 있겠지만 오버라이딩의 특성이나 장점을 활용할 수 없다.
그러므로 final
method 라고 private, static
method는 아니겠지만, private, static
메서드 이면 final
메서드라고 할 수 있다. 이러한 흐름에서 final class
내의 모든 메서드 또한 final
메서드이다.
그렇다면 final
메서드가 오버라이딩을 금지한다
는 점 외에 어떤 의미를 가지게 될까?
메서드 오버라이딩은 메서드 오버로딩, 업-캐스팅과 함께 자바에서 다형성을 구현하기 위해 가지는 방법 중 하나이다. (메서드 오버로딩은 compile time에 결정하지만) 다형성은 동작하는 코드가 컴파일 타임이 아닌 런 타임에 결정 됨을 기본으로 한다. 런타임에 해당 메서드를 실제로 구현하고 있는 객체를 찾는 작업(virtual method invocation procedure)을 통해 실제 객체를 찾아 메서드를 호출하는 것이다. 하지만 final
메서드의 경우 해당 메서드를 구현하는 객체는 하나임을 명시하게 되고 결과적으로 이러한 작업이 필요 없이 어떤 메서드가 호출 되어야 하는지 컴파일 타임에 결정하게 된다.
어떤 코드가 동작해야 하는지 컴파일 타임에 결정 가능 하다는 것은 위와 같이 실제로 동작해야 하는 코드를 런 타임에 찾아야 하는 과정을 거칠 필요가 없다고 명시 해주는 것이며, 이는 JIT 컴파일러가 byte code를 native로 변환할 때 최적화를 수행할 수 있는 힌트를 제공한다.
실제 최적화의 수행 여부, 최적화의 수행 방법(ex. inlining 등) 그리고 이를 통한 최적화의 효과가 다르고, 어차피 JIT 컴파일러가 수행하는 최적화 과정이 이러한 개발자의 힌트에 의해 달라지는 부분이 크지 않아 미미하다는 분석이 있지만 이러한 부분이 존재한다는 것은 알아둘만 하다.
결론
최적화라는 부수적인 효과보다는 의미에 집중해서 사용 여부를 결정하는 방향이 좀 더 효과적일 것이다. 상태에 대한 의존성과 결국은 final
의 특성을 가짐을 알아두고 사용하자.