Lang/Java

형변환과 다형성

soooy0 2024. 12. 4. 20:22

클래스 형변환

  • 타입변환이란 타입을 다른 타입으로 변환하는 것을 말함
  • 클래스의 타입변환은 상속관계에 있는 클래스 사이에서 발생한다.

 

:: 자동 형변환(업캐스팅)

  • 의미 그대로 자동적으로 타입 변환이 일어나는 경우
  • 하위 타입이 상위 타입으로 자동 형변환 가능
  • 상위타입 참조변수 = (상위타입) 하위타입 참조변수; —> 캐스팅 연산자는 생략 가능이며 자동 변환
더보기
더보기

상위 타입 변수 = (상위)하위 타입 객체;

Cat cat = new Cat(); // 하위타입 객체생성

Animal animal = (Animal)cat; //자동으로 하위 타입이 상위 타입으로 형변환이 일어남
  • cat, animal은 타입만 다를 뿐, 동일한 cat 객체의 주소를 참조한다.
  • 상속관계에 있는 클래스들은 자동 타입변환 된다.
자동 형변환 시 접근
class Parent {
	void method1(){///}
	void method2(){///}
}

class Child extends Parent{
	void method2(){///}
	void method3(){///}
}

Child child = new Child();

Parent parent = child; //상위타입 참조변수에 하위타입 객체주소 대입

parent.method1(); //parent꺼 호출됨
parent.mehtod2(); //child꺼 메서드2번 호출됨(오버라이딩)
parent.method3(); //child꺼라서 호출 불가

 

  • 상위 타입이 참조변수가 하위 객체 주소를 참조하고 있지만, 변수로 접근 가능한 멤버는 상위 타입이 가지고 있는 멤버로 한정된다.
  • 여기서 주의할 점은, 오버라이딩 된 메서드는 상위 메서드 대신 하위 메서드가 호출된다는 점.
  • 메서드 3은 상위 타입이 가지고 있는 멤버가 아니므로 호출이 불가한 것이다.
  • 확장 되기 전 더 작은 것(상위)은 확장 되고 난 후 더 큰 것(하위)의 객체를 생성할 수 있지만, 더 큰 것이 더 작은 것의 객체를 생성할 수 없다는 점을 기억하자.
class Tv{}

class CaptionTv extends Tv{}

class Ex{
	public static void main(string[] args){
		
		Tv t = new Tv(); //가능
		Tv t1 = new CaptionTv(); //상위 타입 참조변수로 하위타입 객체 생성 가능
		
		CaptionTv ct = new CaptionTv(); //가능
		CaptionTv ct1 = new Tv(); //하위타입 참조변수로 상위타입 객체 생성 불가능
		
		
		t = ct; // (Tv) t = (Tv) ct, CaptionTv -> Tv로 업캐스팅-자동형변환
		ct = (CaptionTv)t; // (CaptionTv) ct = (CaptionTv)t , 
		//Tv -> CaptionTv로 다운캐스팅, 생략불가
		
	//크기가 더 커진 하위가 크기가 더 작은 상위로 형변환 하는 것은 생략 가능인 것임!
	//크기가 더 작은 상위가 크기가 더 큰 하위로 형변환 하는 것은 생략 불가능
    
}

 

:: 강제 형변환(다운캐스팅)

  • 상위타입이 하위타입으로 형변환 시, 강제 타입변환이 필요함
  • 더보기
    더보기
    하위타입 참조변수 = (하위타입)상위타입 객체 —> 캐스팅 연산자 생략 불가능!
  • 상위 타입 객체를 하위 타입으로 무조건 강제 변환 할 수 있는 것은 아니다. 하위 객체가 상위 타입으로 자동 변환 후, 다시 하위타입으로 변환할 때 강제 타입 변환을 사용할 수 있다.
Parent pa = new Child(); // 자동 타입 변환
Child c = (Child)pa // 강제 타입 변환

 

강제 형변환 접근
  • 하위 객체가 상위 객체로 자동 형변환하게 되면, 상위 타입에 선언된 필드와 메소드만 사용 가능했었다.
  • 강제타입의 경우는 하위타입과 상위타입의 필드와 메소드를 전부 사용 가능하다.
class Parent {
	String field1;
	void method1(){///}
	void method2(){///}
}

class Child extends Parent{
	String field2;
	void method3(){///}
}


Parent parent = new Child(); //상위타입 참조변수에 하위타입 객체 생성 

parent.field1 = "ㅇㅇㅇ"; //상위 타입의 필드 값 변경
parent.method1(); // parent꺼 호출됨
parent.mehtod2(); // parent꺼 호출됨

parent.field2 = "ㄹㄹㄹ" //하위 타입의 필드 값이라서 접근 불가
parent.method3(); //하위 타입의 메소드라서 호출 불가

Child child = (Child)parent; // 상위타입을 강제 형변환하여 하위타입 참조변수에 대입
child.field2 = "ㄹㄹㄹ" //하위 타입의 필드 값 변경 가능
child.method3(); //하위 타입의 메소드 호출 가능

 

 

인터페이스 형변환

  • 인터페이스의 타입 변환은 인터페이스와 구현 클래스간에 발생한다.

인터페이스 자동 형변환

  • 구현객체가 인터페이스 타입로 타입변환 시, 인터페이스 변수에 구현 객체를 대입하면, 구현 객체는 자동으로 인터페이스 타입으로 타입변환된다.
더보기
더보기
인터페이스 변수 = (인터페이스 타입) 구현객체;
  • 상위 클래스가 인터페이스를 구현하고 있다면 하위 클래스도 인터페이스 타입으로 자동타입 변환될 수 있다.
     
  • 인터페이스 A를 구현한 B,C 클래스
    B클래스를 상속한 D클래스
    C클래스를 상속한 E클래스

    B, C, D, E로부터 생성된 객체들은 모두 인터페이스 A로 자동 타입 변환 가능!!! 직, 간접적으로 구현하는 꼴임. 상위 B,C 클래스가 인터페이스 A를 구현하고 있기 때문단, 인터페이스에 3개의 메소드, 구현클래스에 5개의 메소드 선언되어 있다면 인터페이스 타입 변수로 호출 가능한 메서드는 3개뿐이다!

 

인터페이스 강제 형변환

  • 인터페이스가 구현클래스로 타입변환 시, 캐스팅 연산자를 사용해서 인터페이스 타입을 구현 클래스 타입으로 변환시키는 것을 의미 
  • 더보기
    더보기
    구현클래스 변수 = (구현클래스) 인터페이스 변수; >> 캐스팅 연산자 생략 불가

 

메서드 호출 가능한 범위
RemoteControl interface가 가지고 있는 메서드
1. turnOn(){..}
2. turnOff(){..}
3. setVolume(int volume){..}

Television 구현 클래스가 가지고 있는 메서드
4. turnOn(){..}
5. turnOff(){..}
6. setVolume(int volume){..}
7. setTime(){..}
8. record(){..}


💡RemoteControl rc = new Television()
>> 자동 변환되고, 인터페이스에 선언된 메서드(1~3번)만 사용 가능하다. (인터페이스 타입만 사용 가능 한 것, 즉 참조변수의 타입을 봐야함!)

💡Television tv = (Television)rc;
>> 강제 변환이며, 구현클래스에 선언된 메서드(4~8번)를 전부 사용가능하다.

 

다형성

  • 사용방법은 동일하지만 실행 결과가 다양하게 나오는 성질 (ex. 자동차 타이어를 교환하면 한국타이어객체, 금호타이어 객체인지에 따라 성능이 다르게 나옴)
  • 객체 사용 방법이 동일하다는 것은 동일한 메소드를 가지고 있다는 뜻이다. (메소드는 객체 사이 상호작용 수단이다.)
더보기
더보기

ex. 한국 타이어와 금호타이어가 타이어(상위) 메서드를 오버라이딩 하고 있다면,

타이어(상위) 메서드 호출 시, 오버라이딩 된 하위 메소드가 호출된다.

 

이때, 한국 타이어 오버라이딩 메소드와 금호타이어의 오버라이딩 메소드의 내용이 다르기 때문에 실행결과가 다르게 나온다.

이것이 바로 다형성! 즉 자동타입변환 + 메소드 오버라이딩 ⇒ 다형성을 구현하는 것이다.

 

필드 다형성

  • 필드 타입은 동일하지만(사용 방법은 동일), 대입되는 객체가 달라져서 실행 결과가 다양하게 나올 수 있는 것
public class Car{
	// 필드 선언
    public Tire tire;
    
    // 메소드 선언
    public void run(){
    	tire.roll(); // tire 객체에 있는 roll()메서드 호출
    }
}

----이후
// Car클래스 객체 생성
Car myCar = new Car();

// myCar 필드에 대입된 Car클래스 객체, 해당 객체에 있는 tire 필드에 한국타이어 객체 대입
myCar.tire = new HankookTire();

// myCar 필드에 대입된 Car클래스 객체, 해당 객체에 있는 tire 필드에 금호타이어 객체 대입
myCar.tire = new KumhoTire();

// 각 대입된 객체에 따라 해당 객체가 오버라이딩 하고 있는 roll()메서드를 호출하게 되고, 장착된 타이어에 따라 실행결과가 달라진다.
myCar.run()

 

매개변수 다형성

public class Driver{
	public void drive(Vehicle vehicle){
		vehicle.run();
		
	}
}

//1. Vehivle 타입을 호출하기 위해 Vehivle 객체 제공
	Driver driver = new driver();
	Vehicle vehicle = new Vehicle();
	driver.drive(vehicle);
	
//하지만
//Vehicle의 하위객체도 매개변수로 제공할 수 있음
Bus bus 가 Vehicle을 상속받는다고 할 때, dirve 메서드 호출 시,
driver.drive(bus)로 호출 가능하다. 
따라서 어떤 하위 객체가 제공되느냐에 따라 결과가 달라지고,
이것이 곧 다형성이다.

 

 

출처: 이것이 자바다