Java Modifier(제어자)
제어자란
제어자는 클래스, 생성자, 메서드, 필드에 추가되는 키워드로 기능을 추가하며 타깃의 의미를 변경한다.
제어자는 일반적으로 접근제어자와 비접근 제어자 두 그룹으로 분리된다.
접근 제어자
public, protected, private 잘 알려진 제어자로 이 키워드는 클래스, 생성자, 필드, 메서드 등에 적용될 수 있으며 적용되는 타깃에 대한 접근을 제어하는 역할을 한다.
생성자 측면에서는 접근 제어자만 적용이 가능하며 제어자가 존재하지 않으면 package-private이 된다.
비접근 제어자
비접근 제어자는 클래스, 메서드, 필드 기준 으로 나누어서 설명할 수 있다.
클래스 기준
abstract - 클래스를 인스턴스화될 수 없는 클래스로 만드는 제어자
final - 클래스 구현을 마무리해 상속할 수 없게 하는 제어자
static - 내부 클래스에 적용되며 외부 클래스를 인스턴스화 하지 않고는 클래스 인스턴스 생성을 불가능하게 하는 제어자
interface - interface또한 클래스 제어자 제어자
메서드 기준
abstract - 메서드를 추상화하는 제어자
final - 메서드를 상속할 수 없게하는 제어자
static - 메서드를 클래스 레벨로 만드는 제어자
synchronized - 동시에 한 스레드만 접근이 가능하게 하는 제어자
native - 다른 프로그래밍 언어로 구현될 것이라 알려주는 제어자
필드 기준
final - 초기화 이후 필드 값이 변하지 않게 하는 제어자
static - 필드를 클래스 레벨로 만드는 제어자
transient - 객체가 영구적으로 직렬화 되거나 네트워크 전송이 일어나면 필드를 non-serializable로 만드는 제어자
volatile - 애플리케이션에서 데이터 레이스를 방지하고 long/double atomic 유형 필드를 읽고 쓰는 제어자
Reflection을 활용한 제어자 발견 및 분석 방법
Class, Constructor, Method, Field 모두 getModifiers()라는 메서드를 통해서 제어자들에 대한 정보를 가져올 수 있다. 해당 정보들은 특이하게도 정수 값으로 이루어져 있는데 이는 PUBLIC = 1 : 0000 0001, STATIC = 8 : 0000 1000 와 같이 비트로 나타내어 정수를 가진다.
제어자가 여러개 있을 때에는 (PUBLIC | STATIC = 0000 1001) 와 같이 OR 연산을 통해 값을 저장한다.
제어자의 정보체크를 돕기 위해 java.lang.reflect.Modifiers라는 class에 헬퍼 메서드들이 존재한다.
Modifiers.PUBLIC, ABSTRACT 등이 있으며 이를 (Field.getModifiers() & Modifier.PUBLIC) != 0 과 같이 사용할 수도 있고 boolean Modifier.isPublic(int modifiers) 메서드를 활용해 제어자를 하나씩 확인할 수도 있다.
아래와 같은 함수가 있다고 가정해보자.
public static final void println(String str) {
System.out.println(str);
}
위 메서드의 modifier 정보를 가져오기 위해 Method객체에 담은 다음에 getModifiers를 사용해 값을 가져와보자.
Method method = Main.class.getMethod("println", String.class);
int modifiers = method.getModifiers();
System.out.println(Integer.toBinaryString(modifiers));
/**
실행 결과: 11001
*/
비트로 표현이 잘 되는 것을 볼 수 있다. 이제 헬퍼 메서드를 통해서 어떤 제어자를 가지고 있는지도 확인해보자.
Map<String, Predicate<Integer>> checkMethods = Map.of(
"PUBLIC", Modifier::isPublic,
"PRIVATE", Modifier::isPrivate,
"PROTECTED", Modifier::isProtected,
"STATIC", Modifier::isStatic,
"FINAL", Modifier::isFinal,
"NATIVE", Modifier::isNative,
"ABSTRACT", Modifier::isAbstract,
"SYNCHRONIZED", Modifier::isSynchronized
);
for(Map.Entry<String, Predicate<Integer>> entry : checkMethods.entrySet()) {
print(entry.getKey() + ": " + entry.getValue().test(modifiers));
}
/**
STATIC: true
PROTECTED: false
PUBLIC: true
NATIVE: false
FINAL: true
SYNCHRONIZED: false
PRIVATE: false
ABSTRACT: false
*/
위와 같은 코드를 추가해 메서드의 제어자가 무엇인지 확인 할 수 있다.