캡슐화(encapsulation)
접근 제어자를 사용하는 이유
1) 외부로부터 클래스 내부에 선언된 데이터를 보호하기 위해서
2) 데이터 감추기(data hiding): 데이터가 유효한 값을 유지하도록, 비밀번호 같은 데이터를 외부에서 함부로 변경하지 못하도록 하기 위해서 외부로부터 접근을 제한하는 것
이러한 데이터 감추기 -> 객체지향개념의 캡슐화!!
대입연산자를 사용하는 외부 접근을 막고 메서드를 통해서 iv에 간접 접근할 수 있게 함
접근제어자의 범위는 좁으면 좁을수록 좋음(class 내부에서만 쓰는 메서드를 굳이 public으로 할 필요 없음)
예제) 매개변수에 입력할 값에 제한을 걸어야 하는 경우(getter와 setter)
private 자료형 변수명; // private 변수 선언
public getter {
return 변수명; // 변수의 값 반환
}
public void setter (자료형 변수명);
if(변수의 조건) return; // 조건에 맞는 변수만 반환
this.변수명 = 변수명; // 조건에 맞는 변수만 멤버변수의 값으로 변경
get으로 시작하는 메서드는 단순히 멤버변수의 값을 반환하는 일을 한다.
set으로 시작하는 메서드는 매개변수에 지정된 값을 검사해 조건에 맞는 값 일 때 멤버변수의 값을 변경하도록 작성되어 있다.
-> 보통 멤버변수의 값을 읽는 메서드의 이름 'get멤버변수이름' / 멤버변수의 값을 변경하는 메서드의 이름 'set멤버변수이름'
-> get으로 시작하는 메서드 'getter', set으로 시작하는 메서드 'setter'
예제) getterSetter 예문
public class Exercise {
public static void main(String[] args) {
Armor suit1 = new Armor();
Armor suit2 = new Armor("mark6", 180);
System.out.println(suit1.getName() + ":" + suit1.getHeight());
System.out.println(suit2.getName() + ":" + suit2.getHeight());
}
}
class Armor {
private String name ;
private int height;
Armor() { // 생성자
this.name = "mark0";
this.height = 200;
}
Armor(String name, int height) { //생성자 오버로딩
setName(name);
setHeight(height);
}
public void setName(String n) {
this.name = n;
}
public String getName() {
return name;
}
public void setHeight(int h) {
this.height = h;
}
public int getHeight() {
return height;
}
}
예제) 시간
public class TimeClass {
public static void main(String[] args) {
Time2 t = new Time2(11, 23, 49);
t.setHour(t.getHour()+1); // 현재 시간보다 1시간 후로 변경한다.
System.out.printf("%d:%d:%d\n",t.getHour(),t.getMinute(),t.getSecond());
System.out.println(t); // t -> t.toSting()과 같다.
}
}
class Time2 {
private int hour, minute, second;
Time2(int hour, int minute, int second) {
setHour(hour); // 생성자에 setHour 불러오기!
setMinute(minute);
setSecond(second);
}
public int getHour() {
return hour;
}
public void setHour(int hour) { // 여기서 void인데 return이 나오는 이유는 return이 break;역할
if (hour < 0 || hour > 23) return; // 0부터 23 사이의 숫자가 아니면 그냥 return(값 0)
this.hour = hour; // 0 ~ 23 사이의 숫자이면 hour 출력
}
public int getMinute() {
return minute;
}
public void setMinute(int minute) {
if (minute < 0 || minute > 59) return;
this.minute = minute;
}
public int getSecond() {
return second;
}
public void setSecond(int second) {
if (second < 0 || second > 59) return;
this.second = second;
}
public String toString() {
return hour + ":" + minute + ":" + second;
}
}
<결과값>
12:23:49
12:23:49
- Time클래스의 모든 멤버변수의 접근 제어자를 private으로 하고, 이 들을 다루기 위한 public 메서드를 추가했다.(set 메서드) 그래서 t.hour = 13;과 같이 멤버변수로의 직접적인 접근은 허가 되지 않음!
예제) 위의 if문을 isNotValidHour 메서드로 만들기!
class Time {
private int hour; // 0 ~ 23사이의 값을 가져야함
public void setHour(int hour) {
if(isNotValidHour(hour)) return;
this.hour = hour;
}
// 매개변수로 넘겨진 hour가 유효한지 확인해서 알려주는 메서드
// 이 메서드는 Time 클래스 안에서만 쓰기 때문에 private으로!
private boolean isNotValidHour(int hour) {
return hour < 0 || hour > 23;
}
public int getHour() { return hour; }
}
다형성(polymorphism)
- 조상클래스 타입의 참조변수로 자손클래스의 인스턴스를 다루는 것
-> 이때까지는 Tv인스턴스를 다루기 위해서는 Tv타입의 참조변수를 사용했지만 Tv의 자손클래스인 SmartTv가 생기면서 Tv인 조상클래스 타입의 참조변수로 자손클래스 SmartTv의 인스턴스를 다룰 수 있다! => 다형성
-> 참조변수의 타입은 반드시 인스턴스 타입과 일치하지 않을 수도 있다.
-> 둘 다 같은 타입의 인스턴스라도 참조변수의 타입에 따라 사용할 수 있는 멤버의 개수가 달라진다!(사진 참고)
SmartTv s = new SmartTv( ); // 참조 변수와 인스턴스의 타입이 일치
Tv t = new SmartTv( ); // 조상 타입 참조변수로 자손 타입 인스턴스 참조
- 자손 타입의 참조변수로 조상 타입의 객체를 생성할 수 없다.
Tv t = new SmartTv( ); // Ok, 허용
SmartTv s = new Tv( ); // 에러, 허용 안됨
실제 인스턴스 안에 들어있는 값보다 참조변수가 가리키는 값이 많으면 안 된다!!
-> 참조변수가 사용할 수 있는 멤버의 개수는 인스턴스의 멤버 개수보다 같거나 적어야 한다!
-> 가리키는 값이 5개(참조변수) -> 실제 객체에 존재하는 값 7개 OK
-> 가리키는 값이 7개(참조변수) -> 실제 객체에 존재하는 값 5개 에러!)
참조변수의 형변환(1)
- 형 변환을 하는 이유: 참조변수(리모컨)을 변경함으로써 사용할 수 있는 멤버의 개수를 조절하기 위해서
- 객체는 그대로 있고 타입만 일치시킴! (객체가 줄어들거나 하지는 않음)
- 조상 자손 관계의 참조변수만 서로 형변환 가능 ( 조상-자손은 되지만 / 자손 - 자손은 안됨 )
자손 타입 -> 조상타입(Up-casting) : 형변환 생략가능
조상 타입 <- 자손타입(Down-cating) : 형변환 생략불가
- 기본형 변수의 형변화에서 작은 자료형 -> 큰 자료형의 형변환은 생략 가능하듯이 참조형 변수의 형변환도 동일!
-> 생략 가능한 이유(자손 타입의 참조변수를 조상타입의 참조변수로 형변환 하는 것은 멤버의 개수가 실제 인스턴스가 갖고 있는 멤버의 개수보다 적을 것이 분명하기 때문에 생략!) ->> 조상클래스의 멤버개수가 자손클래스의 멤버개수보다 작거나 같다 항상!!!
예제) 조상 클래스인 Car와 자손 클래스인 FireEngine과 Ambulance
class Car {
String color;
int door;
void drive() { // 운전하는 기능
System.out.println("drive");
}
void stop() { // 멈추는 기능
System.out.println("stop");
}
}
class FireEngine extends Car { //소방차
void water() { // 물을 뿌리는 기능
System.out.println("water!");
}
}
class Ambulance extends Car { //구급차
void siren() { // 사이렌 기능
System.out.println("siren");
}
}
FireEngine f = new FireEngine();
Car c = (Car)f // Ok. 조상인 Car타입으로 형변환(생략가능)
FireEngine f2 = (FireEngine)c; // Ok. 자손인 FireEngine타입으로 형변환(생략불가)
Ambulance a = (Ambulance)f; // 에러. 상속관계가 아닌 클래스 간의 형변환 불가
- 참조변수의 f로 FireEngine의 객체(color, door, drive(), stop(), water()를 다 출력할 수 있지만, 4개(Car의 멤버변수와 메서드인 color, door, drive( ), stop( ))만 쓰고 싶다면 참조변수의 형변환을 사용해서 변환!
- 참조변수 c로 FireEngine 객체를 가리키고 있지만 FireEngine의 객체를 다 가리킬 수는 없고 FireEngine이 상속된 멤버만(color, door, drive, stop)만 사용할 수 있음 ex) Car타입의 참조변수로는 FireEngine의 메서드인 water를 호출할 수 없음
참조변수의 형변환(2)
5개(FireEngine) -> 4개(Car) (감소) / 4개(Car) -> 5개(FireEngine) (증가)
-> 형변환 감소는 항상 안전하지만 증가는 불완전함!
Car car = null;
FireEngine fe = new FireEngine();
FireEngine fe2 = null;
fe.water(); // water!
car = fe; // car = (Car)fe; 에서 형변환이 생략
//car.water(); 에러!
fe2 = (FireEngine)car; // 자손타입 <- 조상타입. 형변환 생략 불가
fe2.water(); // water!
-> 형 변환할 때 실제 인스턴스가 무엇인지가 중요하다. 참조변수가 가리키는 인스턴스의 타입이 무엇인지 확인!
Car car = null;
FireEngine fe2 = null;
FireEngine fe2 = (FireEngine)car; // 조상 -> 자손으로 형변환
Car car2 = (Car)fe2; // 자손 -> 조상으로 형변환
car2.drive(); // NullPointerException 밠생
-> 객체를 생성하지 않아서 에러!(car2 객체 xx)
Car c = new Car();
FireEngine fe = (FireEngine)c; // 형변환 실행시 에러 java.lang.ClassCastException
fe.water(); // 컴파일 오케이
-> c가 만든 객체에는 (color, door, drive, stop)가 있는데 water를 불러오니까 에러!
-> 참조변수 car가 참조하고 있는 인스턴스가 car타입의 인스턴스이기 때문에! Car c = new FireEngine( ); 로 바꾸면 해결된다! Car보다 FireEngine이 멤버변수가 더 많기 때문에 에러!
'멀티캠퍼스 풀스택 과정 > Java의 정석' 카테고리의 다른 글
자바의 정석4-1 추상 클래스(abstract class)와 추상메서드 (0) | 2022.01.05 |
---|---|
자바의 정석3-6 instanceof 연산자, 다형성의 장점(1,2) (0) | 2022.01.05 |
자바의 정석3-4 제어자(static, final, abstract, 접근제어자) (0) | 2022.01.01 |
자바의 정석3-3 패키지와 import문 (0) | 2022.01.01 |
자바의 정석3-2 오버라이딩, super 참조변수, super( ) 생성자 (0) | 2022.01.01 |