객체지향 프로그래밍2

박민수·2023년 1월 22일
0

자바의 정석

목록 보기
6/17
post-thumbnail

1. 오버라이딩이란?

조상 클래스로부터 상속받은 메서드의 내용을 변경하는 것

2. 오버라이딩의 조건

오버라이딩하는 메서드는 조상 클래스의 메서드와

(1) 이름이 같아야 한다.
(2) 매개변수가 같아야 한다.
(3) 반환타입이 같아야 한다.

다만

(1) *접근 제어자는 조상 클래스의 메서드보다 좁은 범위로 변경할 수 없다.

만약 조상 클래스의 메서드가 protected라면, 오버라이딩하는 자손 클래스는 protected나 public이어야 한다.

> 접근제어자란

접근제어자는 멤버 또는 클래스에 사용되어 해당하는 멤버 또는 클래스를 외부에서 접근하지 못하도록 제한하는 역할을 한다.
즉 접근 제어자를 사용하는 이유는 클래스의 내부에 선언된 데이터를 보호하기 위해서이다.
데이터가 유효한 값을 유지하도록 또는 비밀번호와 같은 데이터를 외부에서 함부로 변경하지 못하도록 하기 위해서는 외부로부터의 접근을 제한하는 것이 필요하다.
또한 외부에는 불필요한, 내부적으로만 사용되는 부분을 감추기 위해서 사용된다.(캡슐화)
이것을 객체지향개념에선 캡슐화(encapsulation)이라 한다. (자바의 특징 참고)

접근제어자는 4가지 종류가 있다.
1) private : 같은 클래스 내에서만 접근 가능
2) default : 같은 패키지 내에서만 접근 가능
3) protected : 같은 패키지 내에서, 그리고 다른 패키지의 자손 클래스에서 접근 가능
4) public : 접근 제한이 전혀 없다.

접근제어자는 생략가능하며 생략했을 때는 자동으로 default로 설정된다.
때문에 default 일경우에는 접근제어자를 지정하지 않는다.
접근 범위 : private < default < protected < public 순으로 보다 많은 접근을 허용한다

(2) 조상 클래스의 메서드보다 많은 수의 예외를 선언할 수 없다.

단 선언된 예외의 개수 뿐만 아니라

  • 조상 클래스에 IOException, SQLException과 같이 예외를 선언되있을 때, 자손 클래스에는 저 2개 이상의 예외를 선언하거나 (조상보다 많은 수의 예외 선언)

  • Exception과 같이 모든 예외의 최고 조상을 선언 (조상보다 더 높은 범위의 예외 선언)

하면 잘못된 오버라이딩이다.

3. super, super()

(1) super는 자손 클래스에서 조상 클래스로부터 상속받은 멤버를 참조하는데 사용되는 참조변수이다.
멤버변수와 지역변수의 이름이 같을 때, this를 붙여 구별했듯이 상속받은 멤버와 자신의 멤버가 이름이 같을 때는 super를 붙여 구별할 수 있다.

super는 변수만이 아니라 메서드 역시 호출할 수 있다.

class Point {
int x;
int y;

String a() {
	return "a :" + a;
    }
}

class Point2 extends Point {
int z;

String a(){			//오버라이딩
	return "a :" + a + ", z :" + z; 	// 그냥 썼을 경우 
    return super.a() + + ", z :" + z;	// 조상의 메서드 호출시
    }
}    

조상클래스의 메서드에 내용을 추가할때는 super를 사용해서 작업하는 것이 효율적이다.
후에 조상클래스의 메서드가 변경되도 자동적으로 반영되기 때문이다.

(2) super()는 조상 클래스의 생성자를 호출하는데 사용된다.
super()는 생성자의 첫 줄에서 호출되어야 한다.
이유는 자손 클래스의 멤버가 조상 클래스의 멤버를 사용할 수도 있으므로 조상의 멤버들이 먼저 초기화되어 있어야 하기 때문이다.
또한 첫 줄에 호출되지 않으면 컴파일러가 자동적으로 super();를 생성자 첫 줄에 삽입하여 호출한다.

class Point { 		//조상 클래스
int x;
int y;

Point(int x, int y) {
	this.x = x;
    this.y = y;
    }
}

class Point2 extends Point{ 	//자손 클래스
int z;

Point2(int x , int y, int z){
	this.x = x;
    this.y = y;
    this.z = z;
}
}

이 예제를 실행하면 컴파일 에러가 발생한다.
이유는 Point클래스의 생성자에서 Point를 찾을 수 없다는 내용이다.

Point2(int x , int y, int z){
	super(); // 컴파일러가 자동으로 추가
	this.x = x;
    this.y = y;
    this.z = z;
}

컴파일러가 자동으로 추가하는 생성자 때문에 기본 생성자인 Point()를 호출하고 Point클래스에는 Point()가 정의되지 않았기 때문에 에러가 나타난다.

Point2(int x , int y, int z){
	super(x, y); // 조상클래스의 생성자 Point(int x, int y) 호출
    this.z = z;
}

이런식으로 수정하면 에러가 사라진다.

4. 다형성

다형성이란 여러 가지 형태를 가질 수 있는 능력을 의미한다.

다형성 예제를 위한 클래스 생성

class Tv {	//조상 클래스
boolean power; //전원상태(on/off)
int channel; // 채널

void power() { power = !power; }
void channelUp() { ++channel;}
... 생략 ...
}

class CaptionTv extends Tv { //자손 클래스
string text; //캡션을 보여주기 위한 문자열

void displayCaption() { /*내용생략 */}
}

(1)
같은 타입의 참조변수로 참조하는 것과
조상 타입의 참조변수로 참조하는 것의 차이

CaptionTv c = new CaptionTv();
Tv t = new CaptionTv(); //조상타입의 참조변수로 자손 인스턴스 참조

Tv타입의 참조변수로는 CaptionTv인스턴스 중에서 Tv클래스의 멤버들만 사용할 수 있다.

즉 CaptionTv인스턴스인 c는 모든 것을 사용할 수 있지만
Tv인스턴스인 t는 text와 void displayCaption()을 사용하지 못한다.(조상 클래스의 멤버와 메서드만 사용가능)

둘 다 같은 타입의 인스턴스지만 참조변수의 타입에 따라 사용할 수 있는 멤버의 개수가 달라진다.

이는 자손의 멤버의 개수는 조상보다 늘 같거나 많다는 의미가 된다.
(자손이 부모의 멤버를 포함하거나 그 이상을 가짐)

(2)

CaptionTv c = new Tv();

반대로 자손타입의 참조변수로 조상타입의 인스턴스를 참조하는 것은 가능하지 않다.
예를 들어 c가 text와 displayCaption()을 사용하려 하지만 c가 참조하고 있는 Tv타입의 인스턴스에는 text와 caption이 존재하지 않기 때문에 에러가 발생한다.

(3) 참조변수의 형변환

자손타입 -> 조상타입 : 형변환 생략가능(좁은 범위로 가는 것)
자손타입 <- 조상타입 : 형변환 생략불가(넓은 범위로 가는 것)

자손타입의 참조변수가 조상 타입으로 형변환하는 것은 다룰 수 있는 멤버의 개수가 줄어드는 것이므로 문제가 되지 않는다.
때문에 생략이 가능하다.
(자손은 부모보다 멤버가 늘 같거나 많기 때문에)

그러나 조상타입의 참조변수가 자손 타입으로 형변환하는 것은 사용할 수 있는 멤버의 개수가 더 많아지는 것이므로 문제가 발생할 가능성이 있다.

Tv t = null; //조상
CaptionTv c = new CaptionTV(); //자식
CaptionTv c2 = null;

t = c; // 자손 -> 조상 (형변환 생략)
t.power(); // 조상의 메서드라 문제 없음
t.displayCaption(); // 자손의 메서드라 에러 발생

c2 = (CaptionTv)t; //자손타입 <- 조상타입
c2.t.displayCaption(); //자손타입이라 모든 메서드 사용가능

class Tv {	//조상 클래스
boolean power; //전원상태(on/off)
int channel; // 채널

void power() { power = !power; }
void channelUp() { ++channel;}
}

class CaptionTv extends Tv { //자손 클래스
string text; //캡션을 보여주기 위한 문자열

void displayCaption() { /*내용생략 */}
}

자손타입 <- 조상타입으로 형변환시 주의점으로

Tv t = new Tv(); //조상
Tv t2 = null; 
CaptionTv c = null;

t.power(); // 조상 메서드 사용
c = (CaptionTv)t;  // 자손타입 <- 조상타입 여기서 컴파일 에러가 발생
c.displayCaption() // 자손 메서드 사용

형변환시 조상타입의 참조변수를 자손타입의 참조변수로 형변환한 것이기 때문에 문제가 없어보이지만, 문제는 t가 참조하고 있는 인스턴스가 Tv(조상타입)이라는 데 있다.
위에 내용처럼 조상타입의 인스턴스를 자손타입의 참조변수로 참조하는 것은 허용되지 않는다.

Tv t = new Tv();를 Tv t = new CatpionTv();로 
변경하면 에러가 사라질 것이다.
profile
쉽게 쉽게

0개의 댓글