다형성

어려운. 왜 사용해야 합니까?

  • 하나의 부모 개체로 자식 개체를 관리하는 것이 편리하기 때문입니다.

  • 새로운 자식 클래스를 생성해야 하는 경우 자식 클래스의 인스턴스에 대한 소스 코드의 일부를 수정하기만 하면 나머지 소스 코드를 재사용할 수 있습니다.

정당성

  • 객체지향 언어의 특징 중 하나는 다양한 형태를 가지고 있다는 점이다.

  • 한 작업에서 여러 작업을 수행하는 개념
  • 하나의 부모 클래스 타입 참조 변수와의 상속 관계에서 여러 타입의 자식 객체를 참조할 수 있는 상속을 이용한 기술

메모리 구조


업캐스팅(== 프로모션)

  • 상위 유형 참조 변수를 사용하여 하위 개체를 참조할 수 있습니다.

업캐스트 작성 방법

  1. 자식 개체가 부모 개체로 변경되기 때문에 자식 자신의 필드와 메서드를 사용할 수 없습니다.

  2. 다형성을 사용하는 객체 배열
  3. 공공 무효 ex1() { 자동차 자동차 = 새로운 자동차(); Car car2 = new Tesla(); Car car3 = new Spark(); Car() arr = new Car(3); // 부모 타입 참조 변수 배열 선언 및 할당 // 각 배열 요소는 Car 타입 참조 변수 arr(0) = car; // 자동차 주소 == 자동차 객체 // 자동차 참조 변수 arr(1) = car2; // 테슬라 주소 == 테슬라 객체 // 자동차 참조 변수 arr(2) = car3; // 스파크 주소 == 스파크 객체 // 자동차 참조 변수
  4. 다형성과 함께 매개변수 사용
  5. 테슬라 t = 새로운 테슬라(“전기 모터”, “전기”, 4, 1000000); Spark s = new Spark(“콤팩트 엔진”, “가솔린”, 4, 0.5); 자동차 c = new Car(“디젤 엔진”, “디젤”, 12); printCar
  6. 다형성과 함께 반환 유형 사용
  7. 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 타입이 아님)"); }

제본

  • 실행할 실제 메서드 코드를 호출 코드와 연결
		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)의 재정의된 메서드와 바인딩 == 동적 바인딩

동적 바인딩의 장점

  • 업캐스팅 상태에서 참조 변수를 별도의 다운캐스팅 없이 자식의 오버라이드된 메서드를 실행할 수 있다.