✍Polymorphism(다형성)
객체 지향 언어의 특징 중 하나로 ‘다양한 형태를 나타내는 성질’이라는 뜻으로
하나의 행동으로 여러 가지 일을 수행하는 개념.
상속을 이용한 기술로 부모 클래스 타입 참조변수 하나로
상속 관계에 있는 여러 타입의 자식 객체를 참조할 수 있는 기술
➡ 자식 객체안에 부모가 있으니깐
같은 타입의 부모가 아니라 부모참조변수로 자식 객체를 참조한다.
ex) 추상화, 캡슐화, 상속 같은 다양한 성질
- 상속(재사용, 코드길이 감소+ 유지보수) < 다형성(다형성의 전제조건은 상속이기때문에 더 큰 범위)
하나의 객체가 여러 가지 타입을 가질 수 있는 것
하나의 클래스가 메서드가 다양한 방식으로 동작 가능한 것
하나의 참조변수로 여러 객체를
두 개는 도형이라는 특징
도형 중에 원이 있다
도형 중에 삼각형이 있다.
🚩 클래스 형변환
1. Up Casting (업 캐스팅)
상속 관계에 있는 부모, 자식 클래스 간에 부모타입의 참조형 변수가 모든 자식 타입 객체의 주소를 참조할 수 있다.
객체하나가 부모와 자식의 모습을 가질 수 있기때문에 상속 기반의 기술이다.
자식이 부모로 변하였기 때문에 업 캐스팅이라고한다.
자식 객체의 주소를 전달받은 부모 타입의 참조변수를 통해서
접근할 수 있는 객체의 정보는 부모로부터 상속받은 멤버만 참조 가능
예제
Car c2 = new TeslaCar(6, "원형핸들", "전기모터", 50000); // 부모 타입의 참조 변수 = 자식 타입의 객체 // -> 참조변수와 객체의 타입이 불일치하나 대입연산이 가능한 상태이다.
컴퓨터 연산 규칙 : 같은 자료형끼리만 연산이 가능하다.
(Car : 부모 객체 / TelsaCar : 상속 받은 자식 객체)
➡ 부모 타입의 참조 변수 = 자식 타입의 객체
참조변수와 객체의 타입이 불일치하나 대입연산이 가능한 상태이다.
다형성()
➡ Car타입 참조변수가 TeslaCar타입 객체를 차조하는 경우 TeslaCar 내부에 구현된 Car 부분만을 참조할 수 있는 형태로 변화한다.
TeslaCar -> Car == TeslaCar 객체가 2가지 모양을 가지고 있다 == 다형성
➡ TeslaCar는 보이지않고 그 안에 있는 Car를 보고
자료형은 다르지만 TeslaCar 안에 Car가 있기때문에 참조 가능하고 그래서 Car로 대체 됐다.
❗필드 정보를 출력하는데 에러나는 이유
➡ 부모 타입의 참조 변수로는 자식 객체만의 멤버를 참조할 수 없으므로 에러 발생.
위의 두번째 그림처럼 자식 전체를 참조하는것이 아닌 자식 객체 안의 부모만 참조한다.
🔎 다형성 업 캐스팅 활용
1) 객체 배열
: 서로 다른 자식 타입의 객체를 하나의 부모 타입 객체 배열로 모두 다룰 수 있다.
예제
PolymorphismService
example2
public void example2() { //기본 생성자 +초기화 TeslaCar tesla = new TeslaCar(4, "원형핸들", "전기모터", 50000); RollsRoyce royce = new RollsRoyce(4, "원형", "경유", "검정"); Spark spark = new Spark(4, "원형", "휘발유 엔진", 0.5); Car[] arr = new Car[3]; //부모 타입 참조변수 배열 arr[0] = tesla; //tesla == TeslaCar 객체의 주소가 저장되어있다 arr[1] = royce; //RollsRoyce 타입 객체 arr[2] = spark; //Spark; // 해당 배열 인덱스의 엔진값 출력 for(int i = 0; i < arr.length; i++) { System.out.println(arr[i].getEngine()); } }
👉TeslaCar tesla = new TeslaCar(4, "원형핸들", "전기모터", 50000);
: tesla : TeslaCar 객체의 주소가 저장되어있다
👉arr[0] = tesla;
: arr[] : 부모 타입 == Car 타입 참조 변수 // TeslaCar: 자식 객체
tesla == TeslaCar 객체의 주소가 저장되어있다
arr[0] = tesla = new TeslaCar();
➡테슬라의 배터리는 불러올수 있지만 부모타입에는 불러올 수 없다
2) 다형성 업캐스팅을 매개변수에 적용
: 서로 다른 자식 타입의 객체를 하나의 부모 타입 객체 배열로 모두 다룰 수 있다.
예제
PolymorphismService
example2
public void example2() { printEngine(tesla); //자식 객체 참조 주소 printEngine(royce); printEngine(spark); } public Car updateEngine(Car c) { // 반환형 존재하게 //매개변수로 얻어온 Car의 엔진 업그레이드 //1. Car를 얻어오는 데 그 안에 자식인 테슬라, 로이스, 스파크가 올 수 도있다 String newEngine = c.getEngine() + "(개량형)"; //출력: 전기모터(개량형) //현재의 엔진 //합쳐진걸 저장하고 그걸 다시 부르겠다 c.setEngine(newEngine); //위에서 어떤값이 들어올지 모르니 newEngine return c; } }
➡ Car 부모 타입의 매개변수 c
➡ 서로 다른타입이지만 3개의 부모가 car로 똑같으니깐 부모타입으로 받아서 사용한다.
3) 다형성의 업캐스팅을 반환형(return 타입)에 적용
: 서로 다른 자식 타입의 객체를 하나의 부모 타입 객체 배열로 모두 다룰 수 있다.
예제
PolymorphismService
example2
public void example2() { TeslaCar tesla = new TeslaCar(4, "원형핸들", "전기모터", 50000); RollsRoyce royce = new RollsRoyce(4, "원형", "경유", "검정"); Spark spark = new Spark(4, "원형", "휘발유 엔진", 0.5); System.out.println( updateEngine(tesla).getEngine() ); Car c2 = createCar(1);// 1이면 테슬라카가 호출 } //매개변수 다형성 적용 public void printEngine(Car c){ System.out.println(c.getEngine()); //정수를 하나 전달 받아 1이면 TeslaCar 2이면 RollsRoyce 3이면 Spark객체를 생성하며 반환 public Car createCar(int num) { Car c = null; //car 자료형 switch(num) { case 1 : c = new TeslaCar(); break; //다형성 업캐스팅 case 2 : c = new RollsRoyce(); break; case 3 : c = new Spark(); break; } return c; //switch문제서 나온 값 하나를 반환한다. }
👉public Car updateEngine(Car c) { }
: updateEngine이라는 메소드 호출되면 반환되는 자료형 car (car가 가지고있는 주소)의 getEngine을 출력한다.
➡ : 아래 public void printEngine(Car c){
여기서 printEngine은 부모 참조 변수가 자식 객체 참조하는 모양 == 다형성 업 캐스팅
셋 다 부모타입이 같으니 Car자료형으로 한다.
🔎 업 캐스팅의 단점
: 업캐스팅 자식의 객체는 사용할 수 없다는게 업캐스딩의 단점
자식의 멤버를 참조할 수 없다
해결법 : 다운캐스팅
2. Down Casting (다운 캐스팅)
자식 객체의 주소를 받은 부모 참조형 변수를 가지고 자식의 멤버를 참조해야 할 경우,
부모 클래스 타입의 참조형 변수를 자식 클래스 타입으로 형변환 하는 것 자동으로 처리되지 않기 때문에 반드시 자식 타입을 명시하여 형변환
업 캐스팅: 자식 전체가 아닌 자식 안에있는 부모를 참조 자식-> 부모
다운캐스팅 : 부모에서 자식으로 한번 더 변화 부모 -> 자식
조건 : 클래스간의 형 변환은 반드시 상속 관계인 클래스끼리만 가능
public void example3() { Car c = new Spark(); ((Spark)c).getDiscountOffer(); TeslaCar t = new TeslaCar(); //t instanceof TeslaCar System.out.println(c instanceof Car); }
👉 ((Spark)c).getDiscountOffer();
: 부모 타입 참조 변수를 자식 타입으로 형변환하여 자식 부분을 참조 가능하게 한다 == 다운캐스팅
🔎 instanceof 연산자
현재 참조형 변수가 어떤 클래스 형의 객체 주소를 참조하고 있는지 확인 할 때 사용 클래스 타입이 맞으면 true, 맞지 않으면 false 반환
다운 캐스팅은 혼자 쓰이지않고 instanceof랑 같이 쓰이는 경우가 많다.
: 참조변수 c가 참조하고 있는 객체가 Car타입인가? 맞으면 true 아니면 false
== c가 참조하고있는 객체에 Car 객체가 포함되어 있는가?
== c가 참조하고 있는 Spark 객체와 Car 객체는 상속 관계인가?
참고) instance (객체)
Car c = new Spark();
* ex) c instanceof Car :: c가 참조하는 타입이 Car 타입이 맞냐는 의미
public void example3() { //for문을 이용하여 배열요소 하나씩 접근 for(int i = 0; i<carArr.length; i++) { if( carArr[i] instanceof TeslaCar) { //carArr[i] 참조변수 //carArr[i] 한 칸이 다 car배열로 car참조변수로 부모타입이다 => 안에있는 부모만 참조할 수 있다 System.out.println(((TeslaCar)carArr[i]).getBatteryCapacity() ); //getBatteryCapacity 자식꺼이니 참조 못하니깐 자식으로 바꾼다 //TeslaCar로 강제 형변환 } else if( carArr[i] instanceof RollsRoyce) { System.out.println(((RollsRoyce)carArr[i]).getUmbrellaColor() ); } else if( carArr[i] instanceof Spark) { System.out.println(((Spark)carArr[i]).getDiscountOffer() ); } else { //Car인 경우 System.out.println(carArr[i].toString() ); } } }
Car c = new Spark();
* ex) c instanceof Car :: c가 참조하는 타입이 Car 타입이 맞냐는 의미참고) Service 클래스 : PolymorphismService
1) TeslaCar 자료형이면 => 배터리 용량 출력
2) RollsRoyce 자료형이면 -> 우산 색을 출력
3) Spark자료형이면 -> 할인 혜택 비율을 출력
4) Car 자료형이면 -> toString()을 출력
if문으로 각 자동차별로 강제 형변환 후 특징을 같이 출력
➡업캐스팅 다운캐스팅 instanceof가 다 있다.
업 캐스팅을 이용하여 여러 자식 객체를 하나의 객체 배열로 다루다가
각 배열 요소의 자료형에 따라 자료형에 맞는 고유한 기능을 수행하기 위해
instanceof로 자료형을 검사하고
다운 캐스팅으로 형변환하여 고유 기능 수행
🚩Binding (바인딩)
묶음, 연결
실제 실행할 메소드 코드와 메소드 호출부를 연결 시키는 것
메소드 호출시 어떤 메소드와 연결할 것인지
1. 정적 바인딩
상속 관계에 있는 부모, 자식 클래스 간에 부모타입의 참조형 변수가 모든 자식 타입 객체의 주소를 참조할 수 있음
프로그램이 실행되기 전 컴파일 단계에서 메소드와 메소드 호출부를 연결
public void example4() { //바인딩: 실제 실행할 메소드 코드와 메소드 호출부를 연결하는 것 // ex1() // 메서드 호출 // public void ex1() { ...} //메서드 실행 // 1. 정적 바인딩 // - 프로그램 실행 전 컴파일 단계에서 // 메서드 코드와 메서드 호출부를 연결 Car c = new Spark(4, "원형 핸들", "가솔린 엔진", 0.5); //up casting상태 System.out.println( c.toString() );
2. 동적 바인딩
자식 객체의 주소를 받은 부모 참조형 변수를 가지고 자식의 멤버를 참조해야 할 경우,
부모 클래스 타입의 참조형 변수를 자식 클래스로 가진다.
컴파일 시 정적 바인딩된 메소드를 실행할 당시의 객체 타입을 기준으로 바인딩되는 것
예제
public void example4() { Car c = new Spark(4, "원형 핸들", "가솔린 엔진", 0.5); //up casting상태 System.out.println( c.toString() ); }
동적 바인딩 성립 요건
정적 바인딩
: 자식을 거쳐 그 안에있는 부모와 연결
동적 바인딩
: 오버라이딩 되었으니 자식이랑 연결되어라
🚩 abstract class (추상 클래스)
구체적이지 않은 미완성 설계도
미완성된 설계도는 객체로 만들 수 없다.
몸체 없는 메소드를 포함한 클래스 (미완성 설계도) 추상 클래스일 경우 클래스 선언부에 abstract 키워드 사용
추상메소드를 일부러 비워놓고 사용처에서 수정해서 쓰기 위해 존재하는것
[구성]
일반 필드
일반 메소드
추상 메소드
추상 : 불분명, 구체적이지 않다.
추상화: 공통된 부분 추출, 불필요한 부분제거
클래스: 객체의 속성과 기능을 정의한 설계도
표현법
[접근제한자] abstract class 클래스명 { }
➡추상 클래스 -> 객체 생성X / 부모 타입 참조 변수
특징
1. 추상 메서드를 포함하고 있는 미완성 클래스(abstract 키워드 사용)
자체적으로 객체 생성 불가 → 반드시 상속하여 객체 생성
2. abstract 메소드가 포함된 클래스는 반드시 abstract 클래스 abstract 메소드가 없어도 abstract 클래스 선언 가능 ex) Animal 클래스
3. 클래스 내에 일반 변수, 메소드 포함 가능
4. 객체 생성은 안되지만 참조형 변수 타입으로는 사용 가능
5. 생성자를 만들 수 있다.
🔎추상 클래스 장점
상속+ 객체별 기능 수행법 정의
- 상속 받은 자식에게 공통된 멤버(필드, 메서드) 제공. == 상속
- 일부 기능의 구현을 강제화(공통적이나 자식 클래스에 따라 재정의 되어야 하는 기능)
1. 추상 메소드
몸체 없는 메소드 추상 메소드의 선언부에 abstract 키워드 사용 상속 시 반드시 구현해야 하는,
오버라이딩이 강제화되는 메소드
[접근제한자] abstract 반환형 메소드명(자료형 변수명);
➡ 코드를 구현하는 {} 가 없다
상속 받은 자식이 오버라이딩 해야 구현 됨
🚩 Interface (인터페이스)
스케치
클래스와 클래스 간의 접점을 만들어준다.
상수형 필드와 추상 메소드만을 작성할 수 있는 추상 클래스의 변형체 메소드의 통일성을 부여하기 위해 추상 메소드만 따로 모아놓은 것으로 상속 시 인터페이스 내에 정의된 모든 추상메소드 구현해야 함
몸체 없는 메소드를 포함한 클래스 (미완성 설계도) 추상 클래스일 경우 클래스 선언부에 abstract 키워드 사용
구성일반필드일반메서드 이 두개가 없다.
상수 필드
추상메서드
이 두가지만 가진다.
표현법
[접근제한자] interface 인터페이스명 {//상수도 멤버로 포함할 수 있음
public static final 자료형 변수명 = 초기값;
//추상 메소드만 선언 가능
[public abstract] 반환자료형 메소드명([자료형 매개변수]);
//public abstract가 생략되기 때문에
//오버라이딩 시 반드시 public 표기해야 함
}
🔎인터페이스 목적
1. 일관된 인터페이스(작업환경)을 제공
> implements 구문을 이용한 인터페이스 상속을 통해 상속 받은 모든 클래스에서 동일한 메서드를 강제적으로 오버라이딩 하게 한다.2. 클래스간의 접점을 만들어 강제적인 부모, 자식 관계 형성
묵시적으로 메서드는 public abstract
int plus(int num1, int num2); //미완성인 코드를 줄테니 알아서 구현해봐라
이렇게만 써도 되긴한다.
🔎인터페이스 특징
인터페이스 안에 있으면 묵시적으로 메서드는 public abstract
1. 모든 인터페이스의 메소드는 묵시적으로 public abstract
2. 변수는 묵시적으로 public static final
==> 두개 다 써도 되고 안써도 되고~
3. 미완성이기 때문에 객체 생성은 안되나 상속받은 자식객체에서 참조형 변수로는 가능(다형성
특징
1. 모든 인터페이스의 메소드는 묵시적으로 public abstract
// public abstract를 지워도 괜찮다는 뜻
ex) (public abstract) int plus(int num1, int num2);
2. 변수는 묵시적으로 public static final
3. 객체 생성은 안되나 참조형 변수로는 가능(다형성)
4. 일관된 인터페이스(작업환경)을 제공
-> implements 구문을 이용한 인터페이스 상속을 통해 상속 받은 모든 클래스에서 동일한 메서드를 강제적으로 오버라이딩 하게 한다. == 상속받은 자식 클래스의 통일성 형성
5. 클래스간의 접점을 만들어 강제적인 부모, 자식 관계 형성
🔎인터페이스 장점
다형성을 이용하여 상위 타입 역할(자식 객체 연결)
(자식 객체 연결) == Marker Interface
- 상속 받은 자식에게 공통된 멤버 제공.
인터페이스 구현 객체에 공통된 기능 구현 강제화 (== 구현 객체간의 일관성 제공) 공동 작업을 위한 인터페이스 제공 ==> 협업할 때 좋다.
2. Marker Interface (마커 인터페이스)
내용은 없지만 마커처럼 특정 표식을 위한 용도의 인터페이스
-> 표식이 되어있는 클래스를 한번에 취급할 수 있다.
3. 추상클래스와 인터페이스 차이점
구분 추상 클래스 인터페이스 상속 단일 상속 다중 상속 구현 extends 사용(확장) implements 사용(구현) 추상 메서드 abstract 메소드 0개이상 (선택) 모든 메소드는 abstract 붙여아ㅑ함 abstract 명시적 사용 묵시적으로 abstract 객체 객체 생성 불가 객체 생성 불가 용도 참조 타입 참조 타입
extends : 클래스간의 상속, 인터페이스 간의 상속
implements: 인터페이스- 클래스 상속 관계 (구현하다)
클래스 간의 다중 상속은 불가능
인터페이스 - 클래스간의 다중 상속은 가능
'Backend > Java' 카테고리의 다른 글
[Java] Collection_ List (0) | 2021.10.06 |
---|---|
[Java] Scanner 주의사항/ .next()와 .nextline() 차이 (0) | 2021.09.16 |
[Java] Inheritance(상속) (0) | 2021.09.09 |
[Java] OOP 4_Method(메소드), 객체 배열 (0) | 2021.09.08 |
[JAVA] 분기문_break, continue (0) | 2021.09.08 |