ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 다형성 - polymorphism
    JAVA/OOP개념( 다형성 ) 2023. 6. 15. 10:01
    728x90

    개념

    다형성의 개념은 크게 2가지로 나누어 설명할 수 있다.

    • 클래스의 다형성
    • 메소드의 다형성

     

    클래스의 다형성

    하나의 타입의 참조변수로 여러타입의 객체를 참조 가능

    조상클래스 타입의 참조변수로 자손클래스의 인스턴스를 참조 가능하다는 것

     

    메소드의 다형성

    메소드의 다형성은 오버로딩과 오버라이딩 크게 2가지가 있다.

    오버로딩은 메소드의 메소드의 이름은 같지만, 메소드의 매개변수리스트가 다른 경우 중복정의가 가능한 것을 말한다.

    매개변수 리스트라는 것은,

    1. 매개변수의 갯수가 다르거나,

    2. 매개변수의 자료형이 다르거나,

    3. 매개변수의 순서가 다를 때

    를 말한다. 위의 경우, 메소드의 오버로딩이 가능하다.

     

    오버라이딩은 메소드의 재정의를 의미하는데, 이는 상속의 개념과 함께 언급되는 내용이다.

    오버라이딩은 자세히 말해, 부모의 클래스에 있던 메소드를 재정의하는 것을 말한다.

    이에도 몇가지 규칙이 있다.

    1. 부모 메소드와 메소드 이름, 매개변수 리스트가 같아야 한다

    2. 권한 범위가 더 좁아지는 것은 불가!(접근제어자)

     

     

     

    <최상위 부모 클래스>

    public class Vehicle {
    
        private int speed;
    
        public int getSpeed() {
            return speed;
        }
    
        public void setSpeed(int speed) {
            this.speed = speed;
        }
    
        public void displayInfo(){
            System.out.println("----Vehicle 정보----");
            System.out.println("speed: " + speed);
        }
    }

     

    <차상위 부모클래스 extends Car>

    public class Car extends Vehicle {
    
        private int oil;
    
        public int getOil() {
            return oil;
        }
    
        public void setOil(int oil) {
            this.oil = oil;
        }
    
        @Override
        public void displayInfo() {
            System.out.println("----Car 정보----");
            System.out.println("speed: " + getSpeed());
            System.out.println("oil: " + oil);
        }
    }

     

    <자식클래스>

    public class HybridCar extends Car{
    
        private int electricity;
    
        public int getElectricity() {
            return electricity;
        }
    
        public void setElectricity(int electricity) {
            this.electricity = electricity;
        }
    
        @Override
        public void displayInfo() {
            System.out.println("----Car 정보----");
            System.out.println("speed: " + getSpeed());
            System.out.println("oil: " + getOil());
            System.out.println("electricity: " + electricity);
        }
    }

     

     

     

    상속관계는 다음과 같다.

    Vehicle  →  Car  →  HybridCar 

     

     

    클래스의 다형성을 이용하면, 우리는 하나의 클래스 타입으로 모든 하위 자식의 인스턴스를 참조할 수 있다.

    public class Polymorphism01Main {
    
    	public static void main(String[] args) {
    		System.out.println("다형성(Polymorphism)");
    
    		// v1, c1, h1 은 서로 다른 타입
    		// 각각의 타입에 맞는 인스턴스 생성한뒤 대입 (참조)
    		Vehicle v1 = new Vehicle();
    		Car c1 = new Car();
    		HybridCar h1 = new HybridCar();
    
    		// 각각의 타입에서 오버라이딩 된 메소드가 동작
    		v1.displayInfo();
    		c1.displayInfo();
    		h1.displayInfo();
    
    
    		System.out.println();
    		// car1~car3 변수 타입에 관계없이
    		// 인스턴스의 오버라이딩 된 메소드가 '알아서' 동작한다.
    		Vehicle car1 = new Car(); // 조상 <-- 자손 (가능)
    		Vehicle car2 = new HybridCar();
    		Car car3 = new HybridCar();
    
    		car1.displayInfo();
    		//----Car 정보----
    		//speed: 0
    		//oil: 0
    		car2.displayInfo();
    		//----Car 정보----
    		//speed: 0
    		//oil: 0
    		//electricity: 0
    		car3.displayInfo();
    		//----Car 정보----
    		//speed: 0
    		//oil: 0
    		//electricity: 0
    
    //		HybridCar car7 = new Vehicle();  // 자손 <- 조상 (불가)
    
    		System.out.println("\n 프로그램 종료");
    	} // end main()
    
    
    } // end class

    위의 코드에서 처럼 자식의 인스턴스를 new 키워드로 생성하게 되면 부모 타입의 클래스 변수로 참조하더라도,

    본인의 오버라이딩된 메소드를 실행하는 것을 확인할 수 있다.

     

     

    다형성의 유용함

    부모타입의 클래스 변수는 자식 인스턴스로 생성된 것을 참조가 가능하기 때문에,

    아래와 같은 작업이 가능하다

    Vehicle[] cars = new Vehicle[3];
    cars[0] = new Vehicle();
    cars[1] = new Car();
    cars[2] = new HybridCar();
    
    // 하나의 타입의 변수에 여러가지 타입의 오버라이딩 된 메소드가 각각 동작시킬수 있다.
    for (int i = 0; i < cars.length; i++) {
        System.out.println();
        cars[i].displayInfo();
    }

    오버라이딩된 메소드를 위와 같이 실행시키는 것이 가능하다.

    하지만 다형성을 이용하면서 유의해야 하는 점도 존재한다.

     

     

     

    다형성 사용시 유의사항

    Vehicle class > setSpeed() 메소드 존재
    Car class > setOil() 메소드 존재
    HybridCar class > setElectricity() 메소드 존재
    package com.lec.java.oop03;
    
    public class Polymorphism03Main {
    
        public static void main(String[] args) {
            System.out.println("다형성의 어려움");
    
            Vehicle car1 = new Vehicle();
            Vehicle car2 = new Car();
            Vehicle car3 = new HybridCar();
    
            car2.setSpeed(10);
    
    	car2.setOil(100); // compile error
    
            System.out.println("\n 프로그램 종료");
        } // end main()
    
    } // end class

    Car의 인스턴스로 선언한 car2는 왜 setOil()메소드를 사용하지 못할까?

    이유는, 변수의 클래스타입이 Vehicle Class이기 때문이다.

     

    상속을 하게되면 부모의 모든 Field를 자식은 사용이 가능하다.

    이때, 메소드의 오버라이딩이 일어나면 해당 오버라이딩 된 메소드는 위와 같은 선상에 위치하게 된다.

    우리가 위의 경우에서 부모타입으로 선언하는 경우, 다음과 같은 그림이 그려진다.

     

    즉, new Car()로 생성하였다고 하더라도,

    Vehicle class 타입의 범위의 메소드와 맴버변수만을 사용할 수 있게 되는 것이다. 

     

    위의 경우 해결하는 방법은 강제형변환을 사용하여 자식클래스에 정의된 필드를 사용하면 된다.

    package com.lec.java.oop03;
    
    public class Polymorphism03Main {
    
        public static void main(String[] args) {
            System.out.println("다형성의 어려움");
    
            Vehicle car1 = new Vehicle();
            Vehicle car2 = new Car();
            Vehicle car3 = new HybridCar();
    
            car2.setSpeed(10);
    
            ((Car) car2).setOil(50);
    
        } // end main()
    
    } // end class

    위의 방법처럼 강제 형변환을 사용하면 부모클래스타입으로 선언을 한 변수도 자기 자신에 정의된 메소드와 필드를 이요할 수 있게 된다.

     

     

     

    여기서도 주의해야 하는 점이 있다.

    반드시! 생성자를 호출한 인스턴스의 범위만을 사용할 수 있다는 것이다.

    Vehicle car1 = new Vehicle();
    Vehicle car2 = new Car();
    Vehicle car3 = new HybridCar();

    위의 경우, car1은 Vehicle 타입의 Vehicle 생성자로 만들어진 객체이므로, 아무리 Car타입 / Hybrid 타입으로 형변환을 사용하더라도 Car타입 / Hybrid 타입의 메서드와 맴버변수를 사용할 수 없다는 것이다.

    그림으로 한번 봐보자

     

Designed by Tistory.