본문 바로가기
Backend/Java

Java 기초 문법 : 다형성(polymorphism)

by 코딩쥐 2024. 6. 13.

Java에서 다형성이란 조상 타입의 참조 변수로 자손 타입의 객체를 다루는 것을 말한다. 다형성 참조 변수가 사용할 수 있는 멤버의 개수는 인스턴스의 멤버 개수보다 같거나 적어야 한다. 따라서 자손 타입의 참조 변수로 조상 타입의 객체를 다룰 수는 없다.

  • 부모클래스 변수명 = new 자손클래스(); 
package com.example1;

class Parent {
    void method1() {
        System.out.println("부모 클래스 method1");
    }

}

// Child클래스는 Parent 클래스를 상속 받는다.
class Child extends Parent {
    void method1() {
        System.out.println("자식 클래스 method1");
    }

    void method2() {
        System.out.println("자식 클래스 method2");
    }
}

public class Example1 {
    public static void main(String[] args) {
        //부모 클래스 타입의 참조변수를 사용하여 자식 클래스의 객체를 생성한다.
        Parent p = new Child();
        //p의 실제 타입은 Child이므로, 자식 클래스의 method1이 호출된다.
        p.method1(); // 자식 클래스 method1
        
        // Parent 객체에 있는 데이터만 담기 때문에 method2를 사용할 수 없다.
        // p.method2(); // 오류
    }
}

 

참조변수의 형변환

사용할 수 있는 멤버의 갯수를 조절하는 방법으로, 참조변수의 형변환은 인스턴스에 영향을 끼치지 않는다. 조상과 자손 관계의 참조변수는 서로 형변환이 가능하다. 자손타입의 참조변수를 조상타입으로 형변환 하는 경우 (up-casting) 형변환을 생략할 수 있고, 조상타입의 참조변수를 자손타입으로 형변환 하는 경우 (down-casting) 형변환을 생략할 수 없다.

package com.example1;

class Parent {
    void method1() {
        System.out.println("부모 클래스 method1");
    }
}

// Child클래스는 Parent 클래스를 상속 받는다.
class Child extends Parent {
    void method2() {
        System.out.println("자식 클래스 method2");
    }
}

public class Example1 {
    public static void main(String[] args) {
        //부모 클래스 타입의 참조변수를 사용하여 자식 클래스의 객체를 생성한다.
        Parent p = new Child();

        //부모 클래스 타입의 p를 형변환을 통해서 자식 클래스로 변경해서
        Child c = (Child)p;
        //자식 클래스의 method2를 호출할 수 있다.
        c.method2(); //자식 클래스 method2
    }
}

 

instanceof 연산자

참조변수를 형변환하기 전에 형변환 가능여부 확인에 사용한다. 사용이 가능하면 true를 반환한다.

  • 참조변수 instanceof 타입(클래스명)
public class Example1 {
    public static void main(String[] args) {
        //부모 클래스 타입의 참조변수를 사용하여 자식 클래스의 객체를 생성한다.
        Parent p = new Child();

        // 형변환 가능 여부 확인 후 형변환
        if (p instanceof Child) {
            //부모 클래스 타입의 p를 형변환을 통해서 자식 클래스로 변경해서
            Child c = (Child) p;
            //자식 클래스의 method2를 호출할 수 있다.
            c.method2();
        }
    }
}

 

여러 종류의 객체를 배열로 다루기

조상타입의 배열에 자손들의 객체를 담을 수 있다. 배열에 객체를 담아 여러 종류의 객체를 같이 다룰 수 있다. 

 

이 예제는 Vehicle이라는 부모 클래스를 생성하고, 이를 상속받는 여러 자식 클래스인 Car, Bicycle, Bus를 정의한 뒤 각 객체를 배열에 넣고 ride() 메서드를 호출하는 예시이다.

 

Vehicle (부모클래스)

class Vehicle{
    void ride(){
        System.out.println("탄다.");
    }
}

 

Car, Bicycle, Bus (자식클래스)

class Car extends Vehicle{
    @Override
    void ride() {
        System.out.println("차를 탄다.");
    }
}
class Bicycle extends Vehicle{
    @Override
    void ride() {
        System.out.println("자전거를 탄다.");
    }
}
class Bus extends Vehicle{
    @Override
    void ride() {
        System.out.println("버스를 탄다.");
    }
}

 

배열에 넣어 각 객체의 ride()메서드 호출

public class Example1 {
    public static void main(String[] args) {
        //배열로 인스턴스 생성
        Vehicle[] vehicles = new Vehicle[3];

        //객체를 배열에 담기
        vehicles[0] = new Car();
        vehicles[1] = new Bicycle();
        vehicles[2] = new Bus();

        //배열에 담은 객체를 for문을 통해 사용
        for(Vehicle vehicle : vehicles){
            vehicle.ride();
        }
    }
}

 

<<전체 예제>>

더보기
package com.example1;
// 부모클래스
class Vehicle{
    void ride(){
        System.out.println("탄다.");
    }
}

// 자식클래스
class Car extends Vehicle{
    @Override
    void ride() {
        System.out.println("차를 탄다.");
    }
}
class Bicycle extends Vehicle{
    @Override
    void ride() {
        System.out.println("자전거를 탄다.");
    }
}
class Bus extends Vehicle{
    @Override
    void ride() {
        System.out.println("버스를 탄다.");
    }
}
public class Example1 {
    public static void main(String[] args) {
        //배열로 인스턴스 생성
        Vehicle[] vehicles = new Vehicle[3];

        //객체를 배열에 담기
        vehicles[0] = new Car();
        vehicles[1] = new Bicycle();
        vehicles[2] = new Bus();

        //배열에 담은 객체를 for문을 통해 사용
        for(Vehicle vehicle : vehicles){
            vehicle.ride();
        }
    }
}

하이딩(Hiding) 

자식 클래스가 부모 클래스의 static 메서드와 동일한 이름과 매개변수를 가진 static 메서드를 정의했을 때 발생한다. static 메서드의 경우 클래스(method area)에 저장 되기 때문에 참조 변수의 타입에 따라 어떤 메서드가 호출될지 결정된다.

즉, 부모 클래스의 static 메서드는 자식 클래스에서 가려지지만(hiding), 호출 될 때는 참조 변수 타입에 따라 부모 클래스의 static 메서드가 호출된다. static 메서드는 다형성의 영향을 받지 않으며 항상 참조 변수의 타입에 따라 결정된다. 

package com.example1;

// 부모클래스
class Vehicle{
    void ride(){
        System.out.println("탄다.");
    }
    static void stop(){
        System.out.println("Vehicle 클래스 : stop 메서드");
    }
}

// 자식클래스
class Bus extends Vehicle{
    @Override
    void ride() {
        System.out.println("버스를 탄다.");
    }

    //static 메서드를 자식 클래스에서 동일하게 정의함
    static void stop(){
        System.out.println("Bus 클래스 : stop 메서드");
    }
}

public class Example1 {
    public static void main(String[] args) {
        Vehicle veh1 = new Bus();
        veh1.ride(); // 버스를 탄다.
        veh1.stop(); // Vehicle 클래스 : stop 메서드
        // 참조 변수가 Vehicle이기 때문에 Vehicle의 static method가 호출됨

        // 형변환을 통해서 참조 변수가 Bus일 경우
        Bus bus1 = (Bus)veh1;
        bus1.stop(); // Bus클래스 : stop 메서드
        // 참조 변수가 Bus이기 때문에 Bus의 static method가 호출됨
    }
}