프로그래밍 언어/JAVA

[JAVA] 자바의 참조형변환 (레퍼런스 형변환)

Rateye 2021. 1. 19. 14:44
728x90
반응형

[JAVA] 참조형변환(래퍼런스 형변환)

  • 클래스 간의 형변환(참조형 변수끼리의 형변환)
  • 반드시 상속 관계에서만 가능한 변환

업캐스팅 Up Casting

  • 슈퍼클래스 타입으로 서브클래스의 인스턴스를 참조하는 것 서브클래스 인스턴스를 슈퍼클래스 타입으로 변환하는 것

    기본 문법
    슈퍼클래스타입 변수명 = 서브클래스인스턴스;

  • 묵시적(자동) 형변환이 일어남

  • 참조 가능한 영역이 축소됨 (슈퍼클래스 타입에서 선언된 변수, 메서드만 접근 가능함)

  • 하나의 슈퍼클래스 타입으로 여러 서브클래스 인스턴스의 공통된 멤버에 접근 가능하도록 해준다

  • 일반적인 레퍼런스 형변환을 의미

  • 자식(서브클래스) 타입에서 부모(슈퍼클래스) 타입으로 변환 시 "자동 형변환(업캐스팅)"이라고 하며, 부모(슈퍼클래스) 타입에서 자식(서브클래스) 타입으로 변환 시 "강제 형변환(다운캐스팅)"이라고 한다.

class Parent {
	
	public void parentPrn() {
		System.out.println("슈퍼클래스의 parentPrn() 메서드!");
	}
	
}

// Child 클래스 정의 - Parent 클래스를 상속
// childPrn() 메서드 정의 - "서브클래스의 childPrn() 메서드!" 출력
class Child extends Parent {
	
	public void childPrn() {
		System.out.println("서브클래스의 childPrn() 메서드!");
	}
	
}
		int a = 10; // 기본형 int타입 변수 선언
		long l = a; // long 타입 변수에 int 타입값을 전달
		// => int타입 값이 long 타입으로 변환됨
		
		// Child 클래스의 인스턴스(c) 생성
		Child c = new Child(); // 서브클래스의 인스턴스 생성
		// 서브클래스의 인스턴스 c 를 통해 접근 가능한 메서드 : 2개
		c.parentPrn(); // 상속받은 메서드
		c.childPrn(); // 자신의 클래스에서 정의한 메서드
		
		// Parent 타입 변수 p 를 선언하여 Child 클래스의 인스턴스 전달
		Parent p; // 슈퍼클래스 타입 참조 변수 p 선언
		p = c; // 서브클래스의 인스턴스 주소를 슈퍼클래스타입 변수에 전달
		// => Child 타입 -> Parent 타입으로 변환됨(업캐스팅)
		// => 업캐스팅은 자동 형변환이 가능하므로 형변환 연산자 생략 가능
		
		// Parent 타입 변수 p 를 통해 접근 가능한 메서드 : 1개
		p.parentPrn(); // 상속받은 메서드는 호출 가능
		//		p.childPrn(); // 오류 발생. 서브클래스에서 정의한 메서드는 호출 불가능
		// => 업캐스팅 후에는 참조 영역에 대한 축소가 발생하므로
		//    상속된 멤버 외의 서브클래스에서 정의한 멤버는 접근 불가능하게 됨
		//    즉, 서브클래스의 멤버는 보이지 않게됨
		
		System.out.println("p 와 c 의 주소값이 같은가? " + (p == c));

 

다운캐스팅 Down Casting

  • 서브클래스 타입으로 슈퍼클래스의 인스턴스를 참조하는 것 슈퍼클래스 인스턴스를 서브클래스 타입으로 변환하는 것

    기본 문법
    서브클래스타입 변수명 = (서브클래스타입)슈퍼클래스인스턴스;

  • 참조 가능한 영역이 확대됨 (서브클래스 타입에서 선언된 변수, 메서드에 모두 접근 가능함) 단, 실제 존재하지 않는 영역에 대한 접근 위험성이 발생함

  • 묵시적(자동) 형변환이 일어나지 않음! 반드시 형변환 연산자를 사용한 명시적(강제) 형변환 필수

  • 명시적으로 형변환 후에도 실행 시점에서 오류 발생할 수 있음 => 따라서, 일반적인 상황에서의 다운캐스팅은 허용되지 않음 => 이전에 이미 업캐스팅 된 인스턴스를 다시 다운캐스팅하는 경우에만 안전하게 변환이 일어남

// 슈퍼클래스타입 인스턴스(p2) 생성
		Parent p2 = new Parent();
		// 슈퍼클래스타입으로 접근 가능한 메서드 : 1개
		p2.parentPrn();
		
		// 서브클래스타입 변수 선언 및 슈퍼클래스타입 인스턴스 전달
		//		Child c2 = p2; // 오류 발생. 다운캐스팅은 자동 형변환이 지원되지 않음
		// => 명시적(강제) 형변환 필요 = 형변환 연산자 사용
		//		Child c2 = (Child)p2; // 서브클래스타입을 명시하여 강제 형변환
		// => 형변환 후에도 오류 발생(Parent cannot be cast to Child)
		
		// 다운캐스팅을 하게 되면 참조 영역에 대한 확대가 일어남
		//		c2.parentPrn(); // Parent 인스턴스에 존재하는 메서드
		//		c2.childPrn(); // Parent 인스턴스에 존재하지 않는 메서드
		// => childPrn() 메서드는 실제 인스턴스에는 존재하지 않지만
		//    Child 타입 클래스가 알고 있는 메서드이므로 접근 가능하게됨
		//    그러나, 실제 존재하지 않는 메서드를 접근하게 되므로 오류!
		// => 존재하지 않는 영역에 대한 참조의 위험 때문에
		//    다운캐스팅은 자동 형변환이 지원되지 않으며
		//    강제 형변환을 하더라도 실행 시점에서 논리적 오류가 발생함!

 

다운캐스팅이 허용되는 경우

		//		Child c3 = new Child(); // 서브클래스의 인스턴스 생성
		//		Parent p3 = c3; // 업캐스팅
		// 위의 두 문장을 하나로 결합 가능
		Parent p3 = new Child(); // 업캐스팅
		// 슈퍼클래스(Parent) 타입으로 접근 가능한 메서드 : 1개
		p3.parentPrn(); // 상속받은 메서드
		
		// 이미 업캐스팅 된 인스턴스를 다시 다운캐스팅
		//		Child c3 = p3; // 명시적 형변환 필요
		Child c3 = (Child)p3; // 다운캐스팅(실행 시 오류 발생하지 않음!)
		
		// 서브클래스(Child) 타입으로 접근 가능한 메서드 : 2개
		c3.parentPrn(); // 상속받은 메서드
		c3.childPrn(); // 서브클래스에서 정의한 메서드
결론
이전에 이미 업캐스팅 되어 참조영역이 축소되었던 인스턴스를 다시 다운캐스팅을 통해 참조영역이 확대되면 접근 범위에 아무런 문제가 없으므로 사용이 가능하다 따라서, 레퍼런스 형변환 시에는 상속 관계를 고려하여 알맞은 형변환 방식을 선택해서 변환해야 한다!

다형성 Polymorphism

  • 하나의 클래스 타입으로 여러 인스턴스를 참조하는 것
  • 업캐스팅을 의미함 서브클래스에서 메서드 오버라이딩을 수행했을 때 슈퍼클래스 타입으로 업캐스팅 후 메서드 호출 시 오버라이딩 된 메서드가 호출되어 코드는 동일하나 실행 결과가 달라지게 됨
  • 다형성을 통해 코드의 통일성을 향상시킬 수 있으나 해당 인스턴스의 상세 속성에 접근하려면 다운캐스팅을 통해 서브클래스 타입으로 변환한 뒤 접근 가능함

업캐스팅 시 오버라이딩

  • 동적 바인딩 : 코드상의 실행할 메서드와 컴파일 후 실행 시점에서 실행되는 메서드가 달라지는 것
  • 서브클래스에 오버라이딩 된 메서드가 존재할 경우 업캐스팅 후에도 오버라이딩 된 메서드가 호출됨 즉, 메서드 호출 시 참조 타입이 누군지는 중요하지 않고 실제 인스턴스가 누군지가 중요하다
/*
 * Car 클래스 정의
 * - 메서드 정의 : 파라미터 없음, 리턴값 없음
 *   1) speedUp() => "Car 의 속력 증가!" 출력
 *   2) speedDown() => "Car 의 속력 감소!" 출력
 * 
 * Truck 클래스 정의 - Car 클래스 상속
 * - 메서드 정의 : 파라미터 없음, 리턴값 없음
 *   1) dump() => "Truck 의 짐 싣기!" 출력
 * 
 * Taxi 클래스 정의 - Car 클래스 상속
 * - 메서드 정의 : 파라미터 없음, 리턴값 없음
 *   1) lift() => "Taxi 의 승객 승차!" 출력
 *   2) drop() => "Taxi 의 승객 하차!" 출력
 */
class Car {
	
	public void speedUp() {
		System.out.println("Car 의 속력 증가!");
	}
	
	public void speedDown() {
		System.out.println("Car 의 속력 감소!");
	}
}

class Truck extends Car {
	
	@Override
	public void speedUp() {
		System.out.println("Truck 의 속력 증가!");
	}

	@Override
	public void speedDown() {
		System.out.println("Truck 의 속력 감소!");
	}

	public void dump() {
		System.out.println("Truck 의 짐 싣기!");
	}
	
}

class Taxi extends Car {

	@Override
	public void speedUp() {
		System.out.println("Taxi 의 속력 증가!");
	}

	@Override
	public void speedDown() {
		System.out.println("Taxi 의 속력 감소!");
	}

	public void lift() {
		System.out.println("Taxi 의 승객 승차!");
	}

	public void drop() {
		System.out.println("Taxi 의 승객 하차!");
	}
}
		System.out.println("------- Truck -------");
		// Truck 인스턴스 생성 및 접근 가능한 메서드 호출
		Truck truck = new Truck();
		
		// Car 클래스로부터 상속받은 메서드
		truck.speedUp();
		truck.speedDown();
		
		// Truck 클래스에서 정의한 메서드
		truck.dump();
		
		System.out.println("------- Taxi -------");
		// Taxi 인스턴스 생성 및 접근 가능한 메서드 호출
		Taxi taxi = new Taxi();
		
		// Car 클래스로부터 상속받은 메서드
		taxi.speedUp();
		taxi.speedDown();
				
		// Taxi 클래스에서 정의한 메서드
		taxi.lift();
		taxi.drop();
		
		System.out.println("=============================");
		
		System.out.println("------ Truck -> Car 업캐스팅 ------");
		// Truck -> Car 업캐스팅 및 접근 가능한 메서드 호출
		Car car = truck;
		// Truck 클래스에서 speedUp(), speedDown() 메서드를 오버라이딩
		// => 따라서, 업캐스팅 후에도 오버라이딩 된 Truck 의 메서드 호출됨
		car.speedUp(); // 서브클래스에서 오버라이딩 된 메서드 호출됨
		car.speedDown(); // 서브클래스에서 오버라이딩 된 메서드 호출됨
		
		//		car.dump(); // 참조 영역 축소로 상속 멤버 외에 접근 불가
		
		System.out.println("------ Car -> Truck 다운캐스팅 ------");
		// 업캐스팅 된 Car 타입을 다시 Truck 타입으로 다운캐스팅
		//		truck = car; // 다운캐스팅 시 명시적 형변환 필요
		truck = (Truck)car; // 다운캐스팅
		
		// Car 클래스로부터 상속받은 메서드
		truck.speedUp();
		truck.speedDown();
		
		// Truck 클래스에서 정의한 메서드
		truck.dump();
		
		
		System.out.println("------ Taxi -> Car 업캐스팅 ------");
		// Taxi -> Car 업캐스팅 및 접근 가능한 메서드 호출
		car = taxi;
		// Taxi 클래스에서 speedUp(), speedDown() 메서드를 오버라이딩
		// => 따라서, 업캐스팅 후에도 오버라이딩 된 Taxi 의 메서드 호출됨
		car.speedUp();
		car.speedDown();
		
		// 참조 영역 축소로 상속 멤버 외에 접근 불가
		//		car.lift();
		//		car.drop();
		
		System.out.println("------ Car -> Taxi 다운캐스팅 ------");
		// 업캐스팅 된 Car 타입을 다시 Taxi 타입으로 다운캐스팅
		//		taxi = car;
		taxi = (Taxi)car;
		
		// Car 클래스로부터 상속받은 메서드
		taxi.speedUp();
		taxi.speedDown();
				
		// Taxi 클래스에서 정의한 메서드
		taxi.lift();
		taxi.drop();

 

instanceof 연산자

  • 어떤 객체가 특정 클래스 타입의 인스턴스인지를 판별하는 연산자 형변환 가능 여부**(is-a 관계)**를 판별 ex) a is a B => a instanceof B
  • 판별 결과를 boolean 타입으로 리턴 boolean 타입 변수에 결과값을 저장하거나 if문 등의 조건식에 직접 사용하여 판별
  • 판별 결과가 true 이면 형변환이 가능한 관계라는 의미 업캐스팅 또는 다운캐스팅 가능한 관계 모두 true 가 리턴됨

기본 문법

a instanceof B
=> a : 객체(참조변수)
=> B : 클래스명(또는 인터페이스명)
class Parent2 {}

class Child2 extends Parent2 {}

// -------------------------------

class HandPhone {
	String phoneNumber;
	
	public void call() {
		System.out.println("HandPhone 의 전화 기능!");
	}
	
	public void sms() {
		System.out.println("HandPhone 의 문자 기능!");
	}
}

// SmartPhone 클래스 정의 - HandPhone 클래스 상속
class SmartPhone extends HandPhone {
	String osName;
	
	public void kakaoTalk() {
		System.out.println("SmartPhone 의 카톡 기능!");
	}
	
	public void youtube() {
		System.out.println("SmartPhone 의 유튜브 기능!");
	}
}
		Parent2 p = new Parent2();
		
		// if문을 사용하여 Parent2 객체가 Child2 타입으로 변환 가능 여부 판별
		// => p is a Child2 에 대한 판별 수행
		if(p instanceof Child2) {
			// 판별 결과가 true 이면 무조건 Child2 타입으로 변환 가능
			System.out.println("p -> Child2 타입으로 형변환 가능!");
		} else {
			// 판별 결과가 true 이면 어떠한 변환도 불가능
			// => 슈퍼클래스 타입 인스턴스이므로 다운캐스팅 불가!
			System.out.println("p -> Child2 타입으로 형변환 불가능!");
		}
		
		System.out.println("--------------------");
		
		Parent2 p2 = new Child2();
		// => p2 is a Child2 에 대한 판별 수행
		if(p2 instanceof Child2) {
			// 판별 결과가 true 이면 무조건 Child2 타입으로 변환 가능
			// => 서브클래스를 이미 업캐스팅 해 놓은 상태이므로
			//    형변환 가능하다는 true 가 리턴됨
			System.out.println("p -> Child2 타입으로 형변환 가능!");
//			Child2 c = p2; // 자동형변환은 불가능한 관계이므로
			Child2 c = (Child2)p2; // 강제형변환
		} else {
			System.out.println("p -> Child2 타입으로 형변환 불가능!");
		}
		
		System.out.println("=======================================");
		
		// SmartPhone 인스턴스(내폰) 생성
		SmartPhone 내폰 = new SmartPhone();
		
		// 내폰은 SmartPhone 입니까? true
		if(내폰 instanceof SmartPhone) {
			System.out.println("내폰은 SmartPhone 이다!");
			// 그러므로 SmartPhone 타입 변수에 저장 가능
			SmartPhone 동생폰 = 내폰;
			동생폰.call();
			동생폰.sms();
			동생폰.kakaoTalk();
			동생폰.youtube();
		}
		
		System.out.println("-------------------------");
		
		// 내폰은 HandPhone 입니까? true
		if(내폰 instanceof HandPhone) {
			System.out.println("내폰은 HandPhone 이다!");
			System.out.println("그러므로 HandPhone 으로 형변환 가능하다!");
			HandPhone 엄마폰 = 내폰;
			System.out.println("내폰은 HandPhone 의 모든 기능을 포함한다!");
			엄마폰.call();
			엄마폰.sms();
		}
		
		System.out.println("-------------------------");
		
		HandPhone 엄마폰 = new HandPhone();
		
		// 엄마폰은 SmartPhone 입니까? false
		if(엄마폰 instanceof SmartPhone) {
			System.out.println("엄마폰은 SmartPhone 이다!");
		} else {
			System.out.println("엄마폰은 SmartPhone 이 아니다!");
			System.out.println("그러므로 SmartPhone 으로 형변환 불가능!");
//			SmartPhone 내폰2 = 엄마폰;
//			SmartPhone 내폰2 = (SmartPhone)엄마폰; // 실행 시 오류 발생!
			System.out.println("엄마폰은 SmartPhone 의 모든 기능 포함하지 못함!");
		}
		
		System.out.println("-------------------------");
		
		HandPhone 엄마폰2 = new SmartPhone(); // 업캐스팅
		
		// 엄마폰은 SmartPhone 입니까? true
		if(엄마폰2 instanceof SmartPhone) {
			System.out.println("엄마폰은 SmartPhone 이다!");
			System.out.println("그러므로 SmartPhone 으로 형변환 가능!");
//			SmartPhone 내폰2 = 엄마폰2; // 강제형변환 필요
			SmartPhone 내폰2 = (SmartPhone)엄마폰2;
			System.out.println("엄마폰은 SmartPhone 의 모든 기능을 포함");
			System.out.println("따라서 SmartPhone 으로 형변환 후에도");
			System.out.println("정상적으로 SmartPhone 의 모든 기능 사용 가능");
			내폰2.call();
			내폰2.sms();
			내폰2.kakaoTalk();
			내폰2.youtube();
		} else {
			System.out.println("엄마폰은 SmartPhone 이 아니다!");
		}
728x90
반응형

'프로그래밍 언어 > JAVA' 카테고리의 다른 글

[JAVA] 자바의 Static  (0) 2021.01.19
[JAVA] 자바의 추상 클래스 & 메서드  (0) 2021.01.19
[JAVA] 자바의 final  (0) 2021.01.19
[JAVA] 자바의 this & super  (0) 2021.01.19
[JAVA] 자바의 상속 Inheritance  (0) 2021.01.18