java

java 다형성,형변환,동적 바인딩

#풀닢 2023. 1. 2. 11:26

다형성

다양한 타입을 가질 수 있는 성질

하나의 인스턴스가 여러 가지 타입을 가질수 있는 것 의미

다형성은 객체지향 프로그래밍의 3대 특징(캡슐화,상속,다형성)중 하나이며, 객체지향 프로그래밍의 꽃이다.

다형성은 상속을 기반으로 한 기술

 

다형성 코드는 Application에 이렇게 저장해야 한다.

Parent p = new Chlid();

 

다형성이 가지는 특징 부모 레퍼런스가 자식 인스턴스를 참조할 수 있다.

자식레퍼런스로 부모 인스턴스를 참조할수 없다.

	Animal a = new Animal();
	Rabit r = new Rabit(); 이렇게도 가능

Rabbit은 Rabbit 타입이기도 하면서 Animal 타입이기도 한다.Tiger은 Tiger 타입이기도 하면서 Animal 타입이기도 한다.

그렇기 때문에 하나의 타입으로 여러 타입의 인스턴스를 처리할 수도 있기도 하고,

하나의 메소드 호출로 객체 별로 각기 다른 방법으로 동작하게 할 수도 있다.

	Animal a1 = new Rabbit();
	Animal a2 = new Tiger();

 이렇게 동작이 가능해야 한다.

하지만 반대로 Animal은 Animal일 뿐 Rabbit, Tiger가 될 수 없다. 따라서 반대로 작성시 에러가 일어난다

// Rabbit r = new Animal();
// Tiger t = new Animal();

Animal 코드는 아래처럼 적어주고

public class Animal {
	/* 동물은 기본적으로 먹는 행동과 뛰는 행동, 우는 행동을 할 수 있다.
	 * 
	 */
	public void eat() {
		System.out.println("동물이 먹이를 먹습니다.");
	}
	public void run() {
		System.out.println("동물이 달려갑니다.");
	}
	public void cry() {
		System.out.println("동물이 울음소리를 냅니다.");
	}

}

 

Rabbit에서 보자면

동물이 하는 행동을 보다 구체화 하여 행동할 수 있도록

Animal클래스의 메소드를 오버라이딩(바꾸기)한다.

public class Rabbit extends Animal {
	@Override
	public void eat() {
		System.out.println("토끼가 풀을 뜯어먹고 있습니다.");
	}
	@Override
	public void run() {
		System.out.println("토끼가 달려갑니다. 깡총 깡총");
	}
	@Override
	public void cry() {
		System.out.println("토끼가 울음소리를 냅니다. 끼익끼익");
	}		
    //오버로딩
	public void jump() {
		System.out.println("토끼가 점프합니다. 점프~~!");
	}

바인딩이란? 

<연결>이라는 뜻임

자신을 다시 호출했음 이걸 동적 바인딩이라고 한다.
컴파일 당시에는 해당 타입(Animal)의 메소드와 연결 되어 있다. => 정적 바인딩
하지만 런타임 당시에는 실제 객체가 가지는 오버라이딩 된 메소드로 바인딩이 바뀌어 동작한다 => 동적 바인딩

 

동적 바인딩

컴파일 당시에는 해당 타입의 메소드와 연결되어 있다가 런타임 당시 실제 객체가 가진

오버라이딩 된 메소드바인딩이 바뀌어 동작하는 것을 의미

\

성립 조건

상속 관계로 이루어져 다형성이 적용된 경우, 메소드 오버라이딩이 되어 있어야 한다.

Parent p = new Child();

p.methodA();

System.out.println("동적 바인딩 확인 ---------------");
a1.cry(); //이건 어디에 있는 어떤 거야
a2.cry();

현재 레퍼런스 변수의 타입은 Animal이기 때문에 Rabbit과 Tiger가 가지고 있는
 * 고유의 기능을 동작시키지 못한다.

왜냐면 위에서 본 듯이 Animal a1 = new 이렇게 했기 때문이다.

//		a1.jump();
//		a2.bite();

class type casting : 클래스 형변환

객체별로 고유한 기능을 동작시키기 위해 레퍼런스 변수를 형변환하여 Rabbit과 Tiger로 변경해야 메소드 호출이 가능하다.

//		(Rabbit)a1.jump();
		//이렇게 하면 ()가 먼저되서
		((Rabbit)a1).jump();
		((Tiger)a2).bite();
		//괄호로 한번 더 감싸야 한다.

레퍼런스 변수가 참조하는 실제 인스턴스가 원하는 타입과 맞는지 비교하는 연산자 instanceof*/

타입 형변환을 잘못 하는 경우 컴파일 시에는 문제가 없지만 런타임 시 Exception이 발생한다.
 ((Tiger)a1).bite(); => 토끼는 호랑이가 될 수 없다.

//앞쪽에서는 레퍼런스 변수명 쓰고 뒤쪽에는 클래스 타입명

		System.out.println("instanceof 확인-----------------");
		System.out.println("a1이 Tiger 타입인지 확인 : " + (a1 instanceof Tiger));
		System.out.println("a1이 Rabbit 타입인지 확인 : " + (a1 instanceof Rabbit));
		System.out.println("a2이 Tiger 타입인지 확인 : " + (a2 instanceof Tiger));
		System.out.println("a1이 Rabbit 타입인지 확인 : " + (a2 instanceof Rabbit));
		//앞쪽에서는 레퍼런스 변수명 쓰고 뒤쪽에는 클래스 타입명
		/*상속 받은 타입도 함께 가지고 있다. (다형성) */
		System.out.println("a1이 Animal 타입인지 확인 : " + (a1 instanceof Animal));
		System.out.println("a2이 Animal 타입인지 확인 : " + (a2 instanceof Animal));
		/*모든 클래스는 Object의 후손이다.*/
		System.out.println("a1이 Object 타입인지 확인 : " + (a1 instanceof Object));
		System.out.println("a2이 Object 타입인지 확인 : " + (a2 instanceof Object));

instanceof 연산자를 이용해서 해당 타입이 맞는 경우에만 클래스 형변환을 적용한다.

 

instanceof 연산자

현재 레퍼런스 변수가 어떤 클래스 타입의 객체 주소를 참조하고 있는지를 확인할 때 사용

클래스 타입이 맞으면 true 맞지 않으면 false로 반환

if(레퍼런스 instanceof 클래스타입) {

true일 때 처리한 내용, 해당타입으로 다운 캐스팅

}

	if(a1 instanceof Rabbit) {
		((Rabbit)a1).jump();
	}
	if(a2 instanceof Tiger) {
		((Tiger)a2).bite();

클래스 형변환은 up-casting과 down-casting으로 구분할 수 있다.up-casting : 상위 타입으로 형변환

 

업캐스팅

상위(부모)타입으로 형변환

Car c= (Car)new Sonata();


down-casting : 하위 타입으로 형변환

다운캐스팅

하위(자식)타입으로 형변환

((Sonata)c).moveSonata();

묵시적 형변환

다운 캐스팅인 경우

 

// Animal animal1 = new Rabbit(); //이건
//즉 상위타입을 Rabbit으로 업캐스팅 하고 있는 것이다.
//up-casting의 경우 자동형변환(묵시적 형변환)이 적용 된다.

@ @1 = (@) &();

 

//down-casting의 경우 강제 형변환(명시적 형변환)을 해야 한다.*/

& &1 = (&) @;

		//up-casting
		Animal animal1 = (Animal) new Rabbit(); //이라고 하는 형변환이 일어나고 있는거임
		Animal animal2 = new Rabbit();
		
		//down-casting
		Rabbit rabbit1 =(Rabbit) animal1;
//		Rabbit rabbit2 =animal2;
	}

다형성과 객체 배열을 이용해서 여러 인스턴스를 연속 처리할 수 있다.
객체 배열 다형성 적용해서 만들기 여러 인스턴스 적용가능
상위 타입의 레퍼런스 배열을 만들고 각 인덱스에 인스턴스를 생성해서 대입한다.

Animal[] animals = new Animal[5];		
	animals[0] = new Rabbit();
	animals[1] = new Tiger();
	animals[2] = new Rabbit();
	animals[3] = new Tiger();
	animals[4] = new Rabbit();

for을 이용해서 출력하기

Animal 클래스가 가진 메소드를 오버라이딩한 메소드 호출 시 동적 바인딩을 이용할 수 있다.

		for(int i = 0; i< animals.length; i++) {
			animals[i].cry();
			animals[i].eat();
			animals[i].run();
SMALL