어려운. 왜 사용해야 합니까?
- 하나의 부모 개체로 자식 개체를 관리하는 것이 편리하기 때문입니다.
- 새로운 자식 클래스를 생성해야 하는 경우 자식 클래스의 인스턴스에 대한 소스 코드의 일부를 수정하기만 하면 나머지 소스 코드를 재사용할 수 있습니다.
정당성
- 객체지향 언어의 특징 중 하나는 다양한 형태를 가지고 있다는 점이다.
- 한 작업에서 여러 작업을 수행하는 개념
- 하나의 부모 클래스 타입 참조 변수와의 상속 관계에서 여러 타입의 자식 객체를 참조할 수 있는 상속을 이용한 기술
메모리 구조
업캐스팅(== 프로모션)
- 상위 유형 참조 변수를 사용하여 하위 개체를 참조할 수 있습니다.
업캐스트 작성 방법
- 자식 개체가 부모 개체로 변경되기 때문에 자식 자신의 필드와 메서드를 사용할 수 없습니다.
- 다형성을 사용하는 객체 배열
- 공공 무효 ex1() { 자동차 자동차 = 새로운 자동차(); Car car2 = new Tesla(); Car car3 = new Spark(); Car() arr = new Car(3); // 부모 타입 참조 변수 배열 선언 및 할당 // 각 배열 요소는 Car 타입 참조 변수 arr(0) = car; // 자동차 주소 == 자동차 객체 // 자동차 참조 변수 arr(1) = car2; // 테슬라 주소 == 테슬라 객체 // 자동차 참조 변수 arr(2) = car3; // 스파크 주소 == 스파크 객체 // 자동차 참조 변수
- 다형성과 함께 매개변수 사용
- 테슬라 t = 새로운 테슬라(“전기 모터”, “전기”, 4, 1000000); Spark s = new Spark(“콤팩트 엔진”, “가솔린”, 4, 0.5); 자동차 c = new Car(“디젤 엔진”, “디젤”, 12); printCar
- 다형성과 함께 반환 유형 사용
- Car() arr = { createCar(1), createCar(2), createCar(3) }; public Car createCar(int num) { 자동차 결과 = null; switch(num) { case 1 : result = new Car(); 부서지다; 사례 2: 결과 = new Tesla(); 부서지다; 사례 3: 결과 = new Spark(); 부서지다; } 반환 결과; }
다형성
- 승격 + 재정의 → 재정의된 자식 메서드 실행
instanceof 연산자
- 객체의 유형을 확인하는 연산자
- 참조된 개체가 특정 데이터 유형인지 또는 상위 상속 관계인지 확인
- 전)
- // arr(1)이 참조하는 객체가 Tesla이면 참, 그렇지 않으면 false System.out.println(arr(1) instanceof Tesla); // 참 // arr(1)이 참조하는 객체가 Sprak이면 참, 그렇지 않으면 거짓 System.out.println(arr(1) instanceof Spark); // false // arr(1)이 참조하는 객체가 Car이면 참, 그렇지 않으면 false System.out.println(arr(1) instanceof Car); // 진실
다운캐스팅
- 부모 타입 참조 변수가 자식 객체를 참조하는 업캐스팅 상태에서만 진행할 수 있는 기술이며, 부모 타입을 자식 타입으로 “강제 변환”하여 기존의 필드와 메서드를 그대로 사용할 수 있도록 하는 기술 자식 개체의.
다운캐스팅 노트
- getter 메서드를 사용하는 경우: “.” 연산자는 캐스트 연산자보다 우선 순위가 높습니다.
→ 얕은 사본을 사용하십시오. - 참조 유형이 일치해야 합니다.
- 솔루션: instanceof와 함께 사용하십시오!
// Car c1 = new Tesla(); Car c1 = new Spark(); // Spark s1 = (Spark)c1; // 다운 캐스팅 // java.lang.ClassCastException (형변환 예외) // -> c1이 참조하는 객체는 Tesla인데 // Spark 참조변수로 Tesla를 참조하려고 해서 문제 발생 // @@@ 해결 방법 : instanceof와 같이 사용하자!
@@@ if(c1 instanceof Spark) { Spark s1 = (Spark)c1; // 다운 캐스팅 System.out.println("성공"); } else { System.out.println("실패(Spark 타입이 아님)"); } - 솔루션: instanceof와 함께 사용하십시오!
제본
- 실행할 실제 메서드 코드를 호출 코드와 연결
Car c1 = new Car("경유 엔진", "경유", 8);
System.out.println(c1.getEngine());
// Car 객체에 있는 getEngine() 메소드를 호출 == 바인딩
프로그램 “실행 전”
- 컴파일러는 getEngine() 메서드가 Car에 있음을 인식하고 c1.getEngine() 호출 코드를 String edu.kh.poly.ex1.model.vo.Car.getEngine() 메서드 코드와 연결합니다 —> 정적 바인딩
Car c2 = new Spark("경차 엔진", "휘발유", 4, 0.5);
// 업캐스팅 적용 -> 부모 부분만 참조 가능한 상태
System.out.println(c2.toString());
// String edu.kh.poly.ex1.model.vo.Car.toString()
// 참조변수 c2가 Car 타입 이므로
// toString()도 Car의 toString()을 호출 - 정적 바인딩
그런데 실행해보면 자식(Spark)의 toString()이 호출되는 것을 확인할 수 있습니다!
컴파일 시간에 부모(Car)와 바인딩 == 정적 바인딩 “런타임”에 자식(Spark)의 재정의된 메서드와 바인딩 == 동적 바인딩
동적 바인딩의 장점
- 업캐스팅 상태에서 참조 변수를 별도의 다운캐스팅 없이 자식의 오버라이드된 메서드를 실행할 수 있다.