본문 바로가기
Dev/☕ Java

[자바의정석 정리-3] 다형성

by 아아덕후 2022. 8. 3.
반응형
목차
1. 다형성(polymorphism)
2. 형변환
3. Instanceof 연산자
4. 매개변수의 다형성
5. 여러 종류의 객체를 배열로 다루기
6. 추상클래스 , 추상메서드
7. 인터페이스

1. 다형성(polymorphism)

- 조상 타입 참조변수자손타입 객체를 다루는 것

부모 클래스 / 자식클래스

상속의 경우, 위와같이 조상 클래스와 자손 클래스를 생성한 후

기존 객체(인스턴스) 생성시, 위와 같이 참조변수 타입과 객체 타입을 일치시킨다.

하지만 다형성은 아래와 같이 조상(Tv)타입 참조변수(t2)로 자손 타입 객체(SmartTv)를 다루는 것이다.

다형성

Tv t2 = new SmartTv();
조상타입 참조변수 생성 자손타입객체
(조상 – 자손의 관계에서만!)

그렇다면 왜 다형성을 사용하는가? 2가지 장점으로 인해 다형성을 사용한다.


다형성의 장점
1. 다형적 매개변수
2. 하나의 배열여러 종류 객체 다루기

1. 다형적 매개변수
- 참조형 매개변수는 메서드 호출시, 자신과 같은 타입 또는 자손타입의 인스턴스를 넘겨줄 수 있다.-
- https://www.youtube.com/watch?v=U-VGYYH-obM&list=PLW2UjW795-f5JPTsYHGAawAck9cQRw5TD&index=34

2. 하나의 배열로 여러 종류 객체 다루기
- 보통 하나의 종류밖에 담을 수 없는데 다형성을 이용해 여러 종류 객체를 배열로 이용 가능
- https://www.youtube.com/watch?v=pcd29KSrql8&list=PLW2UjW795-f5JPTsYHGAawAck9cQRw5TD&index=35



다형성
- 객체와 참조변수의 타입이 일치할 때와 일치하지 않을 때의 차이?
→ 사용할 수 있는 인스턴스의 멤버 개수 차이로 인해 유연한 사용이 가능하다.

조상 클래스 Tv의 멤버는 Power, channel, power(), channelUp(), channelDown() 으로 총 5개이다.
자손 클래스 SmartTv의 멤버는 조상 클래스 Tv의 멤버를 상속받고, Text, caption()을 추가해서 총 7개이다.

이때 다형성을 통해
Tv t = new SmartTv(); 로 선언했을 경우에
SmartTv의 인스턴스 멤버는 7개이지만 조상 타입의 멤버는 5개이므로
조상 타입 참조변수 t가 사용할 수 있는 멤버는 5개이다.

해당 멤버개수의 차이가 가장 중요한 포인트이다.

[주의]
- 다형성 : 조상 타입의 참조변수로 자손 타입의 객체 가리킨다.
- 하지만, 자손 타입의 참조변수로 조상 타입의 객체를 가리킬 수 없다.(에러)
Tv t = new SmartTv(); → 다형성(Ok)
SmartTv s = new Tv() ; → 에러

Why? → 인스턴스 멤버 개수보다 참조변수 타입의 멤버 개수가 더 적으면 안된다.

자바의정석 youtube 참고


사용할 수 있는 멤버의 개수가 많은데 적게 사용(참조변수 타입의 멤버개수가 적음)할 경우에는 가능,
하지만, 참조변수 타입의 멤버개수보다 사용할 수 있는 멤버의 개수(인스턴스 멤버 개수)가 많으면 다형성 불가.



2. 형변환

- 사용할 수 잇는 멤버의 개수조절하는 것. (How ? 리모컨을 바꿈으로써)
- 조상자손 관계의 참조변수만 서로 형변환 가능하다. (형제관계는없음)
- 참고 : https://www.youtube.com/watch?v=XP8zpt-yFZs&list=PLW2UjW795-f5JPTsYHGAawAck9cQRw5TD&index=31
참조변수의 형변환 하는 이유
→ 참조변수(리모콘)변경함으로써 사용할 수 있는 (인스턴스의)멤버의 개수조절하기 위해서

[형변환 Step]
1) 형변환 가능한지 instanceof를 통해 확인(true)
2) 형변환 실행

다형성 check
1) Tv t = new SmartTv();
→ 조상 타입의 참조변수로 자손 객체 다루기
2) 참조변수의 형변환
→ 리모콘 바꾸기 : 사용할 수 있는 멤버개수 조절
3) instanceof연산자
→ 형변환 가능여부 확인

조상 → 자손의 형변환이 중요한게 아니라
참조변수가 가리키는 실제 객체가 무엇인지, 객체가 가진 멤버 개수를 파악하는게 중요하다.
객체의 멤버개수가 중요.
객체의 멤버개수를 넘으면 안되는걸 명심!

실제 객체가 가진 멤버 개수를 알고 넘지 않도록 해야함.

FireEngine f = new FireEngine();
Car c = (Car) f;
→ 참조변수의 형변환 → f의 값을 c에 넣는 것
→ 타입을 일치시키기 위해서 형변환.

3. Instanceof 연산자

- 참조변수를 형변환하기 전에 형변환 가능여부 확인에 사용한다.
→ 가능하면, true 반환

자손의 instanceof의 경우, 조상에 대해서는 모두 true가 나옴.
→ 해당 타입으로 형변환이 가능하다.

제일 중요한 것
1) Instanceof로 형변환이 가능한지 확인하고 형변환을 해야한다.
2) 조상들에 대해서는 instanceof값은 항상 참을 반환한다.




4. 매개변수의 다형성

- 참조형 매개변수는 메서드 호출시, 자신과 같은 타입 또는 자손타입의 인스턴스를 넘겨줄 수 있다.
기존에는 각 메서드의 매개변수의 차이를 오버로딩을 통해 여러 개의 메서드를 작성했다.
하지만 새로운 매개변수를 추가할 때마다 해당 메서드를 추가하는 일(오버로딩)은 비효율적이며 유지 보수 어려움과 코드 중복이 발생한다

이러한 문제를 해결하기 위해 해당 매개변수를 모두 상속하는 조상을 선언해주는 것이다.

이를 통해 조상의 매개변수 타입을 통해 여러 메서드에 들어가는 매개변수를 상속하는
조상타입의 참조변수(메서드의 매개변수로 들어감)를 통해 자손 객체를 가리켜 코드 중복도를 줄인다.
메서드를 오버로딩하여 여러 개 만들 필요가 없어지며
매개변수가 조상타입의 참조변수인 메서드 하나로 모든 메서드를 사용하게 할 수 있다.

이 코드에서 아래와 같이 하나의 메서드로 변경 가능. (여기서는 2개 이지만, 생활코딩님의 말을 빌리자면 수백, 수억개의 코드가 존재한다고 가정하면 매우 효율적으로 단축시킬 수 있다.)


Tv1, computer를 Product의 자손으로 선언하여
위와같이 다형성을 이용하여 중복코드를 줄여 간결하게 작성할 수 있다.

 

 

5. 여러 종류의 객체를 배열로 다루기

- 조상타입의 참조변수 배열을 사용하면, 공통의 조상을 가진 서로 다른 종류의 객체를 배열로 묶어서 다룰 수 있다.



- Vector 클래스
→ 가변 배열 기능 + 모든 종류의 객체 저장 가능→ 최고 조상인 object[] 배열을 가지고 있어 모든 객체의 배열을 저장할 수 있다. → 배열의 길이를 자체적으로 늘려주기 때문에 배열 크기를 지정하지 않아도 되므로 배열의 길이를 자동으로 관리해준다.


6. 추상클래스 , 추상메서드

참고 : https://www.youtube.com/watch?v=9VgkoVFZvyk&list=PLW2UjW795-f5JPTsYHGAawAck9cQRw5TD&index=36

추상클래스
- 미완성 설계도이다. 이는 미완성 메서드(추상메서드)를 갖고 있는 클래스이다.

추상메서드
- 미완성 메서드이다. 추상 메서드 = 선언부만 있고 구현부( 몸통 { })이 없는 미완성 메서드이다.
- abstract 리턴타입 메서드이름();
- 꼭 필요하지만 자손마다 다르게 구현 될 것으로 예상되는 경우에 사용한다.


추상메서드를 가지고 있으면, 추상클래스이다.
추상메서드 선언부 앞에 abstract를 붙임으로써 몸통이 없어도 에러 나지 않음. (두번째 코드 오류, 세번째 코드 정상 차이)

abstract를 붙임으로써 추후 코드 작성에 해당 메서드를 작성하도록 요청(강제?)하는 기능을 할 수 있다.
(너희들은 상속을 통해 이 추상메서드를 구현해서 필수적으로 써야해! )

- 추상클래스는 미완성이기 때문에 인스턴스(제품)생성 불가하다.
상속을 통해 추상메서드완성하면 인스턴스 생성 가능 (이때, 미완성 메서드 모두 완성해줘야함!! )


이를 통해(추상메서드 상속을 통해 완성하면) 인스턴스 생성 가능하다.

- 그럼 왜 추상클래스를 사용하는가? (추상클래스 사용하는 이유) → 다른 클래스를 작성하는데 도움을 주기 위해서!

추상클래스 Player의 참조변수를 통해서도 AudioPlayer 인스턴스 생성가능하다. (다형성)
왜냐하면 참조변수는 단순히 리모콘이기에 해당 기능만 있으면 사용이 가능하기 때문이다.

추상클래스의 작성
- 여러 클래스에 공통적으로 사용될 수 있는 추상클래스를 바로 작성하거나 기존클래스의 공통 부분을 뽑아서 추상클래스로 만든다.


추상화 ←→ 구체화
추상화된 코드는 구체화된 코드보다 유연하며 변경에 유리하다. ( 말 바꾸기 쉽다.)

미완성설계도 조상을 바탕으로 자손 클래스에서 설계도 완성
→ 효과 : 공통부분에 대해 중복제거 및 추가 부분이 적음
→ 미완성설계도이지만, 해당 추상클래스를 통해 다른 설계도(자손 클래스)의 작성이 쉬워짐
→ 또한 3개의 네모를 다른 모양으로 변경시에도, 추상클래스에서 변경해주면 자손클래스에도 적용되므로 리팩토링에도 유리하다.

계층형 추상클래스
→ 단계별로 추상클래스를 만들어 놓으면 상속받으며 내려갈수록 구체적으로 된다.
→ 장점 : 마지막 단계에서 추가 / 혹은 중간 단계에서 변경 을 통한 리팩토링 및 유지보수에서 편리함 상승

7. 인터페이스

7-1 인터페이스
7-2 인터페이스의 상속
7-3 인터페이스의 구현
7-4 인터페이스를 이용한 다형성
7-5 인터페이스의 장점

- 핵심 : (프로그래밍 관점에서) 추상메서드의 집합
- 구현된 것이 전혀 없는 설계도(껍데기)이다. (모든 멤버가 public)
- 캡슐화에서 변수에 접근하기 위해 메서드를 거치는 과정에서 가장 바깥, 껍데기가 인터페이스이다.
(인터페이스 → 메서드 →변수)

- 추상클래스와 인터페이스의 차이
→ 추상클래스 : 일반 클래스인데 추상메서드를 갖고 있는 클래스. (멤버변수 , 생성자 존재 가능)
→ 인터페이스 : 다른 것 없이 추상메서드만의 집합. (멤버변수 등 없음)


Class를 선언하지 않고 interface로 선언한다.
이때, 값은 상수이다. (변수, iv , cv 가질 수 없다.)

예외없이 interface의 모든 멤버는 public이다.
메서드는 모드 추상(abstract )메서드이며 몸통(선언부만 있고 구현부는 없다.)

또한 인터페이스는 항상 예외없이 public , abstract, final이므로 제어자를 일부 또는 전부 생략 가능하다.




7-2 인터페이스의 상속

- 인터페이스의 조상은 인터페이스만 가능하다. (class와 다르게 Object가 최고 조상아니다.)
- 다중 상속(조상이 여러개)이 가능하다.
→ 추상메서드는 충돌해도 문제 없기 때문이다.
→ 같은 메서드를 2개의 부모가 가지고 있으면(2개의 선언부가 같은 메서드가 다른 구현부를 가지는 경우)
어떤걸 상속 받을지 모르기 때문에 상속을 하나만 받을수 있었지만,
추상메서드는 선언부만 있고 구현부가 없기 때문에
구현부가 다른 메서드가 존재할 수 없기 때문이다.
- 따라서 상속으로 인터페이스 여러 개 받을 수 있다.


7-3 인터페이스의 구현

- 인터페이스도 미완성 설계도이기 때문에 객체를 만들 수 없다.
- 그렇다면 어떻게 완성해서 써야하나?
→ 인터페이스의 구현 : 인터페이스에 정의된 추상 메서드를 완성하는 것.
- 추상 클래스는 extends 상속을 통해 완성하는 것처럼
인터페이스는 implements 구현을 통해서 추상 메서드를 작성(구현)한다.




인터페이스를 구현한다 →인터페이스의 추상 메서드를 구현한다 →추상 메서드들의 몸통을 완성해준다.
→CompleteInterface 클래스는 PlayingCard 인터페이스를 구현했다.



일부 추상 메서드만 구현할 경우, 클래스 앞에 abstrarct를 붙여 준다.

인터페이스를 구현한다’의 의미 → 모든 추상클래스를 완성한다.


[정리]
인터페이스란?
추상 메서드의 집합이다.
→ (추가적으로)상수, static 메서드, default 메서드 추가 됨.

인터페이스의 구현이란?
→ 인터페이스의 추상 메서드의 구현부(몸통 ( {} )을 완성해 주는 것., 미완성 설계도 완성)

 

: 추상 클래스 구현

차이 : implements / extends

추상 클래스와 인터페이스의 공통점은?
→ 추상 메서드를 가지고 있다.
→ 미완성 설계도이다. è 완성되지 않은 메서드, 추상 메서드를 가지고 있다.

추상 클래스와 인터페이스 차이점은?
→ 추상 클래스는 iv를 가질 수 있지만, 인터페이스는 iv를 가질 수 없다.
→ 추상 클래스는 일반적인 클래스와 같이 인스턴스 변수, 생성자, 인스턴스 메서드 등 가질 수 있으며, 추상 메서드를 포함하는 것이다.
하지만, 인터페이스는 클래스와 다르게 인스턴스 변수, 생성자, 인스턴스 메서드 가질 수 없으며 추상 메서드만의 집합(static 메서드, default메서드, 상수)을 가진다.
구현이 전혀 없으며 껍데기만 가지고 있다.



7-4 인터페이스를 이용한 다형성

- 인터페이스도 구현 클래스의 부모(? → 엄밀히 말하면 부모는 클래스만 되긴 하지만 의미가 그렇게 됨)
- 인터페이스로도 다형성이 가능하다.
- 일반 부모 – 자식의 성립처럼 인터페이스도 부모 – 자식의 관계가 성립한다. (의미상으로)
- 해당 인터페이스 타입의 참조변수로 이를 구현한 클래스의 인스턴스 참조할 수 있으며 형변환도 가능하다.

// Fighter는 Fightable 인터페이스를 구현하기 위해서 추상 메서드(move, attack)을 완성해야함
// 또한 Unit 조상 클래스를 상속받음.
// 다중 클래스 이지만 하나의 조상이 인터페이스 이기 때문에 사용 가능.



해당 Fighter 클래스Unit으로부터 상속을 받고 Fightable이라는 인터페이스를 구현한다.
→ 다중상속의 메서드의 충돌 발생 문제점이 있지만, 인터페이스는 구현부(몸통 {})이 없기 때문에 클래스 상속과 인터페이스 구현 두 개의 상속(상속, 구현)이 가능하면서 다중상속의 개념을 사용할 수 있다.
→ 이를 통해 인터페이스도 다형성이 가능하다.

부모 클래스의 참조변수 u를 통한 자손 클래스의 인스턴스 참조
부모 인터페이스 참조변수 f를 통한 자손 클래스의 인스턴스 참조
(단, 인터페이스에서 정의한 메서드만 사용가능! è 다형성 조건)


인터페이스를 매개변수로 갖는 메서드

→ 메서드 호출 시 해당 인터페이스를 구현한 클래스의 인스턴스를 매배변수로 제공해야 한다.

인터페이스를 반환(리턴) 타입으로 갖는 메서드

→ 메서드가 해당 인터페이스를 구현한 클래스의 인스턴스를 반환한다는 의미
→ 해당 인터페이스 완성한 클래스 필요

7-5 인터페이스의 장점

참고 : https://youtu.be/Su4Oqk2SclY
- 1) 두 대상(객체)간의 중간 역할(연결, 대화, 소통)을 한다.
- 2) 선언(껍데기)와 구현(알맹이)을 분리시킬 수 있게 한다.
- 3) 개발 시간을 줄일 수 있다.
→ 직접적인 관계에서는 B를 개발하고 A를 개발해야하지만, A를 인터페이스 I와 연결 시킨 후 B를 개발하면,
B 개발을 기다리지 않아도 A 개발을 완료할 수 있다.
- 4) 변경에 유리한 유연한 설계가 가능하다.
- 5) 표준화가 가능하다. ( 자바 à JDBC(인터페이스) à DB (Oracle, MySQL )
DB를 변경 할 때, DB와 연결 된 JDBC가 바뀌지 않는 한 자바 프로그램 바꾸지 않아도 됨.
- 6) 서로 관계없는 클래스들을 관계를 맺어줄 수 있다.


2) 선언부와 구현부 분리

기존 코드는 위와 같이 선언부와 구현부가 함께 있어 유연하지 않아 변경에 불리하다. (껍데기 + 알맹이)

하지만 아래와 같이 인터페이스를 사용한다면 선언부(인터페이스)와 구현부(알맹이 : 구현 클래스 ) 를 분리함으로써 구현부의 유지 보수 관점에서 변경이 유리하며 유연한 코드로 만들 수 있다.

인터페이스 덕분에 B클래스 가 변경되어도 A 클래스는 바꾸지 않아도 있게 된다. (느슨한 결합)
A가 B를 사용한다. (= A가 B에 의존한다)

 

위의 B클래스를 아래와 같이 인터페이스 I 와 B 클래스 두개로 분리한 것이다.
또한 A 클래스에서 메서드 매개변수로 B를 사용하던 것을 아래 간접적인 관계를 만들며 인터페이스 I로 변경한다.

이를 통해 A 클래스와 B클래스의 관계를 직접적인 관계를 끊어낼 수 있다.
따라서 A B하고 관계가 없어지고 인터페이스 I 와의 관계로 변경된다. (선언과 구현을 분리)

인터페이스 I에서 B 클래스의 메서드를 추상메서드로 선언
알맹이는 B클래스 거의 그대로로 동일하지만, 선언부에서 I를 구현한다라는 선언을 추가했다.
이를 통해 추후 B 클래스(알맹이, 구현부)를 변경해야할 때
직접적인 관계 (위의 그림)에서는 A 클래스와 B 클래스 모두 변경해야했지만, 아래에서는 B클래스만 변경하면 된다.


- 포인트 → B 변경시, A 클래스도 변경 하는지 안하는지

객체지향의 특징 → 변경에 유리하며 변경할 것을 줄인다.
- 서로 관계없는 클래스들을 관계를 맺어줄 수 있다.


[정리]

1. 다형성 : 조상 타입 참조변수로 자손타입 객체를 다루는 것
Tv t2 = new SmartTv();
조상타입 참조변수 생성 자손타입객체

2. 참조변수의 형변환(리모콘 바꾸기) → 사용가능한 멤버개수 조절
3. instanceof 연산자 → 형변환 가능여부 확인

참조변수의 형변환 하는 이유
→ 참조변수(리모콘)을 변경함으로써 사용할 수 있는 멤버의 개수 조절하기 위해서

참조변수의 타입은 인스턴스의 타입과 일치하지 않아도 된다.
→ 조상 타입의 참조변수로 자손 타입의 객체를 가리킬 수 있다.

참조변수가 조상타입일 때와 자손타입일 때의 차이
→ 참조변수로 사용할 수 있는 멤버의 개수가 달라진다.
→ 자손 타입의 참조변수로 조상 타입의 객체를 가리킬 수 없다.



[참고]

- 자바의 정석
- 자바의 정석 youtube
- https://www.youtube.com/watch?v=fw7Nm_li0pE&list=PLW2UjW795-f5JPTsYHGAawAck9cQRw5TD&index=30
- -https://www.youtube.com/watch?v=XP8zpt-yFZs&list=PLW2UjW795-f5JPTsYHGAawAck9cQRw5TD&index=31
- https://www.youtube.com/watch?v=9VgkoVFZvyk&list=PLW2UjW795-f5JPTsYHGAawAck9cQRw5TD&index=36

- https://www.youtube.com/watch?v=pcd29KSrql8&list=PLW2UjW795-f5JPTsYHGAawAck9cQRw5TD&index=35
- https://www.youtube.com/watch?v=U-VGYYH-obM&list=PLW2UjW795-f5JPTsYHGAawAck9cQRw5TD&index=34
- https://youtu.be/Su4Oqk2SclY

 

반응형

댓글