Dev/Java

[Java] 다형성

kjyyjk 2022. 7. 21. 20:02

다형성이란 같은 타입이지만 실행 결과가 다양한 객체를 이용할 수 있는 성질을 말한다.

 

다형성을 통하면 하나의 타입에 여러 객체를 대입함으로써 다양한 기능을 이용할 수 있다.

 

자바에서는 다형성을 위해 부모 타입에 모든 자식 객체가 대입될 수 있게 한다.

public class PhoneTest{

    Phone phone1 = new IPhone();
    Phone phone2 = new SPhone();	//자식 객체가 부모의 타입인 Phone에 대입되었다.

}

 

자동 타입 변환은 프로그램 실행 도중에 자동적으로 타입 변환이 일어나는 것을 말한다.

 

자식은 부모의 특징과 기능을 상속받기 때문에 부모와 동일하게 취급될 수 있다는 것이 바로 자동 타입 변환의 개념이다.

 

public class Animal{
}

public class Cat extends Animal{
}

Cat cat = new Cat();

Animal animal1 = cat;	//cat객체를 생성하고 animal1변수에 대입하면 자동타입변환된다.

 

이 경우 cat 변수와 animal1 변수는 타입만 다를 뿐이지, 동일한 cat 객체를 참조하고 있다.

 

이러한 타입 변환은 바로 위의 부모가 아니더라도 상속 계층에서 상위 타입이라면 일어날 수 있다.

 

 

만약 부모 타입으로의 자동 타입 변환을 했다면, 부모 클래스에 선언된 필드와 메서드에만 접근 가능하다.

 

즉 변수는 자식 객체를 참조하긴 하지만, 변수를 통해 자식 객체의 필드나 메서드에는 접근할 수 없다.

 

예외적으로 부모 타입으로 자동 타입 변환한 변수가 자식 객체의 메서드에 접근할 수 있는 경우가 있다.

 

바로 자식 클래스에서 오버 라이딩 했을 경우이다.

 

자식 클래스에서 오버라이딩 되었다면, 자식 클래스의 메서드가 대신 호출된다.

class Animal{
	public void walk(){
    	System.out.println("동물이 지나갑니다.");
    }
}

class Cat extends Animal{
	public void walk(){			// Animal 클래스의 walk() 메서드를 오버라이딩했다.
    	System.out.println("고양이가 지나갑니다.");
    }
}
public class Test {
	public static void main(String[] args) {
		Cat cat = new Cat();  //Cat 클래스로부터 cat 객체 생성
		Animal animal = cat;  //animal변수에 cat객체 대입->animal변수는 cat객체 참조
            //자동 타입 변환의 특징 상 animal 변수는 Animal의 멤버에만 접근가능
		animal.walk();
	}
}

자동 타입 변환의 특징 상 animal 변수는 Animal 클래스의 멤버에만 접근 가능하므로, 

 

animal.walk()를 실행하면 "동물이 지나갑니다" 를 출력해야한다.

 

하지만 자식 클래스인 Cat 클래스에서 walk 메서드를 재정의, 오버라이딩 했기 때문에 결과는 다음과 같다.

 

 

자동 타입 변환은 메서드를 호출할 때에도 많이 발생한다.

 

class User{
    public void use(Phone phone){
        phone.turnOn();
    }
}
public class Test {
	public static void main(String[] args) {
        User user = new User();
        
        iPhone iphone = new iPhone();
        sPhone sphone = new sPhone();
        
        User.use(iphone); //메서드의 매개변수로 자식객체가 들어와 자동타입변환
        User.use(sphone);
        
        //또는
        User.use(new Phone());
        User.use(new iPhone());
        User.use(new sPhone());
        
	}
}

이처럼 매개값의 자동타입변환과 메서드 오버라이딩을 통해 매개변수의 다형성을 구현할 수 있다.

 

 

앞에서 자식 타입을 부모 타입으로 자동타입변환을 하게 되면 자식 클래스의 멤버를 사용할 수 없다는 것을 배웠다.

 

만약에 꼭 자식 클래스의 필드나 메서드를 사용해야 한다면, 강제 타입 변환을 이용하면 된다.

 

class Parent {
    String field1;
    
    void method1(){}
    void method2(){}
}


class Child extends Parent{
    String field2;
    
    void method3(){}
}
public class Test{
    public static void main(String[] args){
        Parent parent = new Child(); // 자동타입변환(자식타입 -> 부모타입)
        
        parent.field1 = "data"
        parent.method1();
        parent.method2();
        
        /*
        parent.field2="data"
        parent.method3();   자동타입변환 시에 자식클래스의 멤버x
        */
        
        Child child = (Child) parent; //강제타입변환(부모타입 -> 자식타입). child변수로
        
        child.field2="data"
        child.method3();   //자식 클래스 멤버 접근 가능
       
    }

}

 

자식 클래스의 멤버를 이용하기 위해 부모타입에서 자식타입으로 다시 강제 타입 변환하는 방법을 확인했다.

 

주의사항으로는 자식 타입이 부모 타입으로 변환되어있는 상태에서만 강제 타입 변환을 할 수 있다.

 

만약 부모 타입의 변수가 부모 객체를 참조할 경우에는 자식 타입으로 변환하지 못한다.

 

이 경우를 피하기 위해서는 부모 변수가 참조하는 객체가 자식 객체인지, 부모 객체인지 확인해보아야 한다.

 

instanceof 연산자를 이용하면 확인할 수 있다.

 

boolean 변수 = 객체 instanceof 타입

우항의 타입으로 객체가 생성되었다면 true를 반환
public void method(Parent parent){
    if (parent instanceof Child){ //Child타입으로 parent가 참조하는 객체 생성된것이면 true
                                  //true이면 자동타입변환되었다는 것을 의미
        Child child = (Child) parent;	//실행 : true이면 강제타입변환
    }									
}