쉽게 쉽게

객체지향언어(캡슐화, 다형성) 본문

개발공부/Java

객체지향언어(캡슐화, 다형성)

곱마2 2023. 3. 17. 15:43
반응형

이 글은 '자바의 정석'의 내용을 기반으로 공부한 내용을 덧붙인 글입니다.


1. 캡슐화

캡슐화란 클래스 안에 서로 연관 있는 속성과 기능들을 하나의 캡슐(capsule)로 만들어 데이터를 외부로부터 보호하는 것을 말한다.

자바 객체 지향 프로그래밍에서 캡슐화를 구현하기 위한 방법은 접근제어자(access modifiers)를 활용하는 방법이 있다.

접근제어자는 클래스 또는 클래스의 내부의 멤버들에 사용되어 해당 클래스나 멤버들을 외부에서 접근하지 못하도록 접근을 제한하는 역할을 한다.

접근제어자란

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

 

접근제어자는 4가지 종류가 있다.

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


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

2. 다형성

다형성이란 여러 가지 형태를 가질 수 있는 능력을 의미한다.
대표적인 예로 우리가 앞서 본 오버라이딩(overriding)과 오버로딩(overloading)이 있다.

★ 오버라이딩(overriding)이란?

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

조상으로부터 상속받은 메서드를 그대로 사용할 수도 있지만, 자식 클래스에서 상황에 맞게 변경해서 사용해야할 경우 오버라이딩이 필요하다.

(1) 오버라이딩의 조건

조상으로부터 받은 메서드의 내용만 변경하기 때문에 오버라이딩하는 메서드는 조상 클래스의 메서드와

메서드 이름이 같아야 한다.
매개변수가 같아야 한다.
반환타입이 같아야 한다.

다만

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

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

 

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

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

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

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

 

오버라이딩을 통해 조상으로부터 물려받은 메서드를 상황에 맞게 변경, 수정하여 사용할 수 있게된다.

★ 오버로딩(overloading)이란?

한 클래스 내에 같은 이름의 메서드를 여러 개 정의하는 것을 '오버로딩'이라 한다.

(1) 오버로딩의 조건

메서드 이름이 같아야 한다.
매개변수의 개수 또는 타입이 달라야 한다.
반환타입은 오버로딩을 구현하는데 아무런 영향을 주지 못한다.

또한 접근 제어자도 자유롭게 지정해 줄 수 있다.

각 메소드의 접근 제어자를 public, default, protected, private으로 다르게 지정해줘도 상관없다. 

결국 오버로딩은 매개변수의 차이로만 구현할 수 있다.

(2) 오버로딩의 장점

만약 메서드도 변수처럼 이름만으로 구별된다면, 한 클래스 내의 모든 메서드들은 이름이 달라야 한다.
이는 이름을 짓는 것도, 사용하는 쪽에서 사용할 메서드를 일일이 기억하는 것도 어렵게 만든다.
오버로딩을 통하면 기억하기도 쉽고 하나의 이름으로 메서드 이름을 절약할 수 있어서 편리하다.

 

같은 기능을 하는 메소드를 하나의 이름으로 사용할 수 있다.

println이라는 메서드에 매개변수로 int, double, boolean, String 등의 다양한 타입을 넣어도 동일한 결과를 나타내는 이유는 오버로딩되어 있기 때문이다.

 

헷갈릴 수 있겠지만 오버라이딩과 오버로딩을 한 마디로 정의하면 이렇다.

오버라이딩 - 상속받은 메서드를 재정의 하는 것(있던 메서드를 수정해서 사용)

오버로딩 - 기존에 없는 새로운 메서드를 추가하는 것(이름이 동일한 새로운 메서드 추가)

 

즉 오버라이딩과 오버로딩을 통해서 같은 이름의 메서드나 변수를 사용하지만 속성이나 기능이 그 맥락에 따라 다른 역할을 수행할 수 있도록 한다.

즉 다형성을 통해 코드의 가독성과 유연성을 높일 수 있다.

 

이제 다형성에서 주의할 점이 무엇인지에 대해 알아본다.

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

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();)의 차이

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

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

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

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

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

(2) 자손타입의 참조변수로 조상타입의 인스턴스를 참조

CaptionTv c = new Tv();

반대로 자손타입의 참조변수로 조상타입의 인스턴스를 참조하는 것은 가능하지 않다.
예를 들어 c가 displayCaption()을 사용하려 하지만 c가 참조하고 있는 Tv타입의 인스턴스에는 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();로 
변경하면 에러가 사라질 것이다.

공부하면서 참고한 블로그입니다.

https://hyoje420.tistory.com/14

 

[Java]오버로딩 & 오버라이딩(Overloading & Overriding)

오버로딩(Overloading) 오버로딩(Overloading)이라는 뜻은 사전적으로 '과적하다.'라는 뜻이다. C언어에서는 함수명이 고유하게 존재해야 한다. 즉 하나의 함수가 하나의 기능만을 구현해야 한다는 것

hyoje420.tistory.com

https://kadosholy.tistory.com/94

 

[Java] 자바 - 오버로딩과 오버라이딩 (Overloading vs Overriding)

자바 - 오버로딩과 오버라이딩 (Overloading vs Overriding) 자바에서 오버로딩과 오버라이딩은 객체지향언어의 특성중 다형성에 해당되는것으로 간략한 예제와 함께 알아보도록 하겠습니다. 목차 오

kadosholy.tistory.com

잘못된 내용이 있다면 지적부탁드립니다. 방문해주셔서 감사합니다.

 

반응형

'개발공부 > Java' 카테고리의 다른 글

예외처리  (0) 2023.03.20
인터페이스  (0) 2023.03.20
객체지향언어(특징, 추상화, 상속)  (1) 2023.03.16
배열  (0) 2023.03.14
연산자  (0) 2023.03.14