난이도 : 하.
작성자 : 옥민수. (http://blindlibrary.tistory.com), (niceminsu@gmail.com)
Original Reference : http://blindlibrary.tistory.com/entry/Java-SE-접근-제한자-클래스-생성자-접근-제한에-관한-고찰
당신이 작성한 클래스 코드는 public으로 도배되어 있는가?
뛰어난 프로그래머는 public을 그다지 좋아하지 않는다. 왜냐하면 접근 가능하다는 것은 복잡도를 증가시키기 때문이다.
접근 제한이란 결국 어떻게 하면 조금 더 단순한 클래스 인터페이스를 만들 수 있을까 하는데서 나오지 않았나 생각된다.
생성자 접근 제한에 관해 이야기 하기 전 접근 제한자에 대한 기본적인 사항을 집고 넘어가자.
Java 접근 제한자에는 일반적으로 4가지가 있다.
- public : 모든 곳에서 접근 가능.
- protected : 상속받은 클래스와 동일 패키지 내부에서 접근 가능.
- default : 동일 패키지.
- private : 외부에서 접근 불가.
이 중에서 잘 쓰이지 않는 접근 제한자가 default 접근 제한자이다.
자바를 얕게 배운 사람이나 자바 개발 초보들은 default 접근 제한이 존재하는지 조차 모른다.
심지어 대학에서 자바 강의 시험을 볼 때 접근 제한의 종류를 묻는 문제가 나왔을 경우 public, protected, private 만 써도 정답 처리가 되는 상황이다.
또한 실제 코드상에는 버젓이 default 접근제한이 쓰이고 있으면서도 대부분 이 접근 제한을 private 과 동일하게 인식하고 만다.
하지만 default 접근 제한은 엄연히 private 접근 제한과 구별되며 private 접근 제한보다 느슨하다.
default 접근 제한. 바로 접근 제한필드를 비워둠으로서 선언하는 묵시적 접근 제한인 것이다.
default 접근 제한의 경우 접근 같은 패키지 내부의 참조만 허용하며 오직 클래스 내부에서만 접근할 수 있는 private과 구별된다.
한 가지 더 잊지 말아야 할 것은 protected 선언은 default선언을 포함한다는 것이다. 즉 protected로 선언되어 있다 하더라도 같은 패키지 내에서 접근하는 것이라면 굳이 상속받지 않아도 접근 가능하다.
하지만 이것은 코드분석을 어렵게 하는 요인이기도 하니 protected로 선언되어 있다면 같은 패키지 내부에 있다 하더라도 상속받아서 접근하도록 하자.
접근 제한이 선언되는 곳은 Class 본체 정의시 , 메소드 정의시, 멤버 변수 정의시, 그리고 생성자 정의시 에 선언된다.
Class 본체의 접근 제한자 선언은 클래스의 선언에 관계된다. 단, 클래스 접근 제한은 private과 protected를 선언할 경우 컴파일 에러를 일으키게 된다.
그 이유는 두 접근 제한자가 선언되었을 경우 논리적으로 모순된 상황에 빠지기 때문이다.
private class foo 로 선언되어 있다면 애초에 쓸 수 없는 클래스가 되니 말 할것도 없다. 다른 모든 곳에서 선언조차 안 되는 클래스를 어떻게 쓴단 말인가?
protected class foo 인 경우를 생각해 보자. 같은 패키지 내부에서라면 문제가 없겠지만, 다른 패키지에서 이 클래스를 상속받기 위해서는 이 클래스를 상속받는다고 선언되어야 한다.
즉, extends foo 가 되어야 하지만, foo를 상속받는다고 선언 하지 못하게 된다. 왜냐하면 아직 상속받지 못한 상태이므로 foo의 선언을 할 수가 없기 때문이다.
클래스 접근 제한이 선언이 아니라 생성에 제한이 걸린다면 protected class foo 는 가능할 것이다.
하지만 선언에 제한을 거는 자바 문법 구조상 상속 선언이 되어야 하기 때문에 다른 패키지의 클래스에서는 상속 받지 못하게 되므로 protected class는 쓸모가 없고 코드 모순을 발생시킬 우려가 있으므로 protected 접근 제한은 class 접근 제한으로 사용 할 수 없도록 자바 문법은 만들어 졌다.
이제 이번 포스팅의 메인 주제인 생성자 접근 제한에 대해 생각 해 보자.
class 접근 제한은 public 과 default만 가능했다. 그 이유로는 객체 선언에 접근 제한이 걸리기 때문이었다.
선언은 가능하되 객체 생성에 제한을 거는 방법. 그것이 바로 생성자 접근 제한이다. 또한 생성자 접근 제한을 이용해서 좀 더 다양한 형태의 프로그램 구조를 만들어 낼 수 있다.
또한 잊지 말아야 하는 것은 생성자 역시 다형성을 지원한다는 것이다.
먼저 private 생성자 를 살펴보자.
public class Foo{
private Foo(){
//any code
}
}
와 같은 형태의 생성자다. inner class 만들어서 쓰는 걸 싫어한다면 유용할 지 모르겠다.
inner class를 만들게 되면 클래스가 하나 더 로드되니 그것보다는 private 생성자를 이용해 자가 객체를 만들어 어떻게서든 클래스를 하나라도 더 줄여보고자 한다면 쓸 수 있겠다.
필자는 private 생성자를 써 본 일 없으니 혹시라도 쓰면 좋은 상황이나 유용함을 알고 있다면 feedback 해 주길 바란다.
다음으로 default 생성자와 protected 생성자를 살펴보자.
public class Foo{
Foo(){
//any code
}
}
public class Foo{
protected Foo(){
//any code
}
}
와 같은 형태의 생성자다. 패키지 내부에서만 사용되는 기능이 있는 클래스라거나 패키지 내 최상위 상속 클래스라면 default 생성자를 정의해 주는 것이 프로그램 구조상 좋다.
이와 같은 상속 관계의 클래스 구조가 있을 경우 sample.master.MasterFoo 클래스의 생성자를 protected로 하기보다는 default로 하는 편이 좋다.
클래스가 아닌 생성자 접근 제한을 default로 할 경우 다른 패키지에서도 MasterFoo를 선언 할 수 있기 때문에 상속 상위 클래스 추상화를 잃어버리지 않는다는 장점이 있다.
패키지 내부와 외부의 기능 차이를 만들고 싶다면 역시 좋은 선택이다.
MasterFoo의 객체를 다른 패키지의 클래스에서 생성할 때 다른 기능을 하도록 만들 필요성이 있다면 생성자 오버로딩(overloading)을 이용해 public으로 정의 해 주면 된다.
MasterFoo는 쿵푸 고수다.
MasterFoo는 자신이 머무는 도장(sample.master)안에서는 사랑하는 두 제자에게 쿵푸를 전수해 주는 사부지만 도장 밖(other package)에서는 약자의 편에서 싸우는 영웅이다.
이 이야기에서 MasterFoo의 기능은 패키지에 따라 명확히 나뉜다. 사부와 영웅. 하지만 MasterFoo는 하나여야 한다.
protected로 도장 밖에 제자를 만들 일이 없다면 default 접근 제한 생성자로 해결된다고 생각하지 않는가?
반면에 도장 밖에 제자를 만드는 SubFoo1, SubFoo2는 protected생성자를 가지는 게 좋은 선택이라고 본다.
또한 protected 생성자의 경우 생성받은 클래스만이 생성 가능한 super()함수가 있기 때문에 상속 받은 클래스의 관점에서 부모 클래스의 역할을 정의 할 수 있다는 장점이 있다.
public 생성자는 특별히 언급하지 않더라도 모두가 잘 사용하고 있다.
결국 생성자 접근 제한은 default와 protected 접근 제한 사용에 무게가 실려 있다. 이 둘을 적절히 잘 사용한다면 객체지향 프로그래밍의 장점인 추상화와 정보 은닉을 더욱 잘 살릴 수 있을 것이다.
또한 프로그램 구조적으로도 조금 더 안정된 형태를 만들 수 있지 않을까 생각된다.
'Past > computing' 카테고리의 다른 글
WOC 2008 . (4) | 2008.11.17 |
---|---|
New authentication algorithem likes OTP (2) | 2008.05.21 |
Sun / MySQL Launching Seminar (4) | 2008.05.13 |