자바에서 말하는 '정보'는 클래스의 '인스턴스 변수'를 의미하므로 정보를 은닉한다는 것은 인스턴스 변수를 숨긴다는 뜻
만약 인스턴스 변수의 직접적인 접근을 허용하면, 컴파일 과정에서 드러나지 않는 중대한 실수가 발생할 수 있음
이러한 오류는 실행 결과에서도 드러나지 않아 더 큰 문제가 됨
예) 메소드 정의를 통한 인스턴스 변수를 초기화하지 않고 직접적으로 인스턴스 변수에 접근하는 경우
class Circle {
double rad = 0;
final double PI = 3.14l
public Circle(double r) {
setRad(r);
}
public void setRad(double r) {
// 반지름은 0보다 작을 수 없음
if(r < 0) {
rad = 0;
return;
}
rad = r;
}
}
class UnsafeCircle {
public static void main(String args[]) {
Circle c = new Circle(1.5);
c.setRad(2.5);
// 좋지 않은 접근 방법, 그리고 문제가 되는 부분
c.rad = -4.5;
}
}
private 선언
클래스를 대상으로 정보를 은닉하기 위해서는 인스턴스 변수 앞에 private 선언을 추가하면 됨
그리고 해당 인스턴스 변수에 접근할 수 있는 메소드를 별도로 제공하면 정보 은닉이 완료됨
인스턴스 변수가 private으로 선언되게 되면 클래스 내에 정의된 메소드 내에서의 접근만을 허용하게 됨
그러므로 클래스 외부에서 private으로 선언된 멤버에 접근할 경우 컴파일 오류가 발생하게 됨
인스턴스 변수를 private으로 선언하게 되면, 필요에 따라 인스턴스 변수에 대한 값의 설정을 위한 Setter 메소드와 값의 참조를 위한 Getter 메소드를 제공해야 함
Getter는 인스턴스 변수의 값을 참조하는 용도로 정의된 메소드로 변수의 이름이 name일 때, 메소드의 이름은 getName으로 짓는 것이 관례
Setter는 인스턴스 변수의 값을 설정하는 용도로 정의된 메소드로 변수의 이름이 name일 때, 메소드의 이름은 setName으로 짓는 것이 관례
class Circle {
private double rad = 0;
final double PI = 3.14l
public Circle(double r) {
setRad(r);
}
// rad에 값을 저장 (수정)
public void setRad(double r) {
// 반지름은 0보다 작을 수 없음
if(r < 0) {
rad = 0;
return;
}
rad = r;
}
// rad에 저장된 값을 반환
public doubel getRad() {
return rad;
}
}
class InfoHideCircle {
public static void main(String args[]) {
Circle c = new Circle(1.5);
c.setRad(2.5);
// 컴파일 오류 발생
c.rad = -4.5;
}
}
✔ 접근 수준 지시자
네 가지 종류의 접근 수준 지시자
접근 수준 지시자의 종류는 public, protected, private, default로 네 가지가 존재
이러한 선언을 할 수 있는 대상으로는 클래스의 정의, 클래스의 인스턴스 변수와 메소드가 존재
클래스 정의 대상의 접근 수준 지시자 : public, default
클래스가 public으로 선언되면 위치에 상관없이 어디서든 해당 클래스의 인스턴스를 생성할 수 있음
이때 하나의 소스파일에 하나의 클래스만 public으로 선언할 수 있으며, 소스파일의 이름과 public으로 선언된 클래스의 이름을 일치시켜야 함
클래스가 default로 선언되면 동일 패키디로 묶인 클래스 내에서만 인스턴스를 생성할 수 있음
package zoo;
// default
class Duck {
...
}
// public
public class Cat {
public void makeCat() {
// 같은 패키지로 묶여 있으므로 인스턴스 생성 가능
Duck quack = new Duck();
}
}
package animal;
// public
public class Dog {
public void makeCat() {
// public으로 선언되어 있으므로 인스턴스 생성 가능
zoo.Cat yaong = new zoo.Cat();
}
public makeDuck() {
// 같은 패키지로 묶여 있지 않으므로 인스턴스 생성 불가로 컴파일 오류 발생
zoo.Duck quack = new zoo.Duck();
}
}
인스턴스 변수와 메소드 대상의 접근 수준 지시자 : public, protected, private, defualt
인스턴스 멤버가 public으로 선언되면 어디서든 접근이 가능함
인스턴스 멤버가 protected로 선언되면 상속 관계에 있는 다른 클래스에서 접근이 가능함
인스턴스 멤버가 private로 선언되면 동일 클래스에 정의된 메소드에서만 접근이 가능함
인스턴스 멤버가 default로 선언되면 동일 패키지로 묶인 클래스 내에서만 접근이 가능함
package zoo;
public class Cat {
// 어디서든 호출 가능
public void makeSound() {
...
}
// 동일 패키지로 묶인 클래스 내에서만 호출 가능
void makeHappy() {
...
}
}
package animal;
public class Dog {
public void welcom(zoo.Cat c) {
// 호출 가능, 컴파일 성공
c.makeSound();
// 호출 불가, 컴파일 오류
c.makeHappy();
}
}
package alpha;
public class AAA {
protect int num;
}
public class ZZZ extends alpha.AAA {
public void init(int n) {
num = n;
}
}
Access-level Modeifiers
✔ 캡슐화
캡슐화란?
정보 은닉과 더불어 객체지향 기반의 클래스 설계에 있어 가장 기본이면서 중요한 원칙 중 하나
하나의 목적을 이루기 위해 관련 있는 모든 것을 하나의 클래스에 담아 두는 것
무조건 많이 담는다고 해서 캡슐화가 아니며, 부족해도 안되고 넘쳐도 문제가 됨
클래스들을 적절히 캡슐화시키면 프로그램이 간결해짐
캡슐화가 이뤄지지 않은 예제
캡슐화가 정상적으로 이뤄지지 않았을 때는 각각의 인스턴스를 생성해야 하며, 클래스 선언의 순서와 같은 제약사항이 있을 경우 문제가 발생하게 됨
class SinivelCap {
public void take() {
System.out.println("콧물 감기가 낫습니다!");
}
}
class SneezeCap {
public void take() {
System.out.println("재치기가 낫습니다!");
}
}
class SnuffleCap {
public void take() {
System.out.println("코감기가 낫습니다!");
}
}
class ColdPatiend {
public void takeSinivelCap(SinivelCap cap) {
cap.take();
}
public void takeSneezeCap(SneezeCap cap) {
cap.take();
}
public void takeSnuffleCap(SnuffleCap cap) {
cap.take();
}
}
public class NotEncapsulation {
public static void main(String[] args) {
ColdPatiend suf = new ColdPatiend();
suf.takeSinivelCap(new SinivelCap());
suf.takeSneezeCap(new SneezeCap());
suf.takeSnuffleCap(new SnuffleCap());
}
}
하나의 클래스로 캡슐화
하나의 클래스 안에 관련된 모든 내용이 캡슐화되면 하나의 인스턴스만 생성하면 되고, 순서 과정이 모두 자동화되게 됨
class SinusCap{
void sniTake(){
System.out.println("콧물이 싹 낫습니다.");
}
void sneTake(){
System.out.println("재채기가 멎습니다.");
}
void snuTake(){
System.out.println("코가 뻥 뚫립니다.");
}
void take(){
sniTake();
sneTake();
snuTake();
}
}
class ColdPatient{
void takeSinusCap(SinusCap cap){
cap.take();
}
}
class goodEncapsulation{
public static void main(String[] args){
ColdPatient suf = new ColdPatient();
suf.takeSinus(new SinusCap());
}
}
포함 관계로 캡슐화
한 클래스가 다른 클래스의 인스턴스를 멤버로 가지는 포함 관계를 이용해 캡슐화를 완성하는 과정에서 사용할 수 있음
class SinivelCap {
void take() {
System.out.println("콧물 감기가 낫습니다!");
}
}
class SneezeCap {
void take() {
System.out.println("재치기가 낫습니다!");
}
}
class SnuffleCap {
void take() {
System.out.println("코감기가 낫습니다!");
}
}
class SinusCap{
SinivelCap siCap = new SinivelCap();
SneezeCap szCap = new SneezeCap();
SnuffleCap sfCap = new SnuffleCap();
void take(){
siCap.take();
szCap.take();
sfCap.take();
}
}
class ColdPatient{
void takeSinusCap(SinusCap cap){
cap.take();
}
}
class goodEncapsulation{
public static void main(String[] args){
ColdPatient suf = new ColdPatient();
suf.takeSinus(new SinusCap());
}
}