JAVA

[ Java ] - 상속과 다형성

algml0703 2022. 9. 4. 13:42
반응형

상속과 다형성

상속

class B extends A {}

상위 클래스의 속성이나 기능을 하위클래스가 상속받아 속성이나 기능이 확장된 클래스를 구현하는 것을 의미한다. class B extends A {}는 하위클래스 B가 상위클래스 A를 상속받은 것이다.

** 일반적으로 상위클래스가 하위클래스보다 일반적으로 구현되며, 하위클래스에서 더욱 구체화되는 특성을 가진다.

** 자식클래스는 부모클래스의 메서드를 재정의할 수도, 그 기능을 확장할 수도 있다.

** extends 뒤에는 하나의 클래스가 올 수 있다.

** 자바에서는 기본적으로 자식클래스의 객체가 인스턴스화 될 때 부모클래스의 디폴트 생성자를 호출한다. super 생성자는 자식클래스에서 부모 클래스의 생성자를 호출하는 방법이다. 또한 자식클래스가 부모클래스의 구성요소에 접근할 때에도 super 키워드를 사용한다.

** 하위클래스는 상위클래스에 존재하지 않는 필드와 메서드를 정의하여 기능을 추가할 수 있으며, 오버라이딩을 통해 상위클래스에서 구현된 메서드를 재정의할 수 도 있다. 재정의 할때는 메서드명 반환타입 파라미터가 모두 동일해야 한다.

** 접근제어자 protected를 사용한 경우 상위클래스를 상속받은 하위클래스에서 protected로 선언한 것에 접근할 수 있다.

** 접근제어자를 선언해주지 않은 경우에는 동일 패키지내에서 함께 존재할때에만 접근할 수 있다.

** 자식클래스가 부모클래스의 메서드를 재정의해줄 때 접근지정자의 범위는 더 넓거나 같아야 한다.

** 하위클래스가 생성될때, 하위클래스 이전에 상위클래스가 먼저 생성된다.

** 컴파일 과정에서 컴파일러가 하위클래스 호출시 super(); 키워드를 통해 상위클래스를 먼저 호출하게 된다. super 키워드는 서브클래스에서 명시적으로 슈퍼클래스의 생성자를 선택하여 호출할 때 사용된다. super키워드는 상위클래스의 주소를 가리킨다.

** 부모 클래스에서 접근지정자가 protected로 된 구성요소의 경우 자식 클래스의 구성요소가 된다.

** 자식 클래스는 부모클래스의 protected, public으로 선언된 것에 대하여 this 접근이 가능하다. (반면에 부모클래스에서 private으로 선언된 경우에는 자식 클래스에서 접근할 수 없다.)

** 오버로딩은 하나의 클래스내에서 이루어지며 오버라이딩은 상속관계에서 이루어진다.

ex)

public class Shape {
    protected int x;
    protected  int y;
    protected String color;

    public void draw() {
        System.out.println("Drawing Shape");
    }
}

~ 부모클래스인 Shape에 접근지정자 protected로 선언된 필드에 대해 하위클래스에서 this를 통해 자신의 구성요소처럼 접근할 수 있다.

public class Rectangle extends Shape{
    private int width;
    private int height;

    public void resize(int width, int height) {
        this.width = width;
        this.height = height;
    }
    // 부모클래스인 Shape에 접근지정자 protected로 선언된 필드에 대해 하위클래스에서 this를 통해 자신의 구성요소처럼 접근할 수 있다.
    public void move(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

 

상속 관계

  • is-a 관계

일반적인 개념을 구체적 개념으로 하는 경우

  • has-a 관계

한 클래스가 다른 클래스를 소유한 경우 

접근제한자 가시성  (  public > protected > default > private )

  동일패키지 외부클래스 다른패키지  동일패키지 하위클래스 다른패키지 하위클래스
public o o o o
private x x x x
protected o x o o
default(none) o x o x

ex) 상위클래스인 customer 클래스 생성 후, customer 클래스를 상속받는 하위클래스 vipCustomer, coldCustomer 생성

customer 클래스

package inheritance;

public class Customer {
	protected int customerID;
	protected String customerName;
	protected String customerGrade;
	int bonusPoint;
	double bonusRatio;
	
	public Customer() {
		customerGrade = "SILVER";
		bonusRatio = 0.01;
        
        	System.out.println("customer 호출");
	}
	
	/*
	
	public Customer(int customerID, String customerName) {
		this.customerID = customerID;
		this.customerName = customerName;
		customerGrade = "SILVER";
		bonusRatio = 0.01;
	}
	*/
	
	public int calcPrice(int price) {
		bonusPoint += price * bonusRatio;
		return price;
	}
	public String showCustomerInfo() {
		return customerName + "님의 등급은 " + customerGrade +"이며, 보너스 포인트" +bonusPoint +"입니다.";
	}

	public int getCustomerID() {
		return customerID;
	}

	public void setCustomerID(int customerID) {
		this.customerID = customerID;
	}

	public String getCustomerName() {
		return customerName;
	}

	public void setCustomerName(String customerName) {
		this.customerName = customerName;
	}

	public String getCustomerGrade() {
		return customerGrade;
	}

	public void setCustomerGrade(String customerGrade) {
		this.customerGrade = customerGrade;
	}

	public int getBonusPoint() {
		return bonusPoint;
	}

	public void setBonusPoint(int bonusPoint) {
		this.bonusPoint = bonusPoint;
	}

	public double getBonusRatio() {
		return bonusRatio;
	}

	public void setBonusRatio(double bonusRatio) {
		this.bonusRatio = bonusRatio;
	}
	

}

vipcustomer 클래스

package inheritance;

public class VipCustomer extends Customer {
	
	private int agentID;
	private double saleRatio;
	
	public VipCustomer () {
    		// super();
		customerGrade = "VIP";
		bonusRatio = 0.05;
		saleRatio = 0.1;
        
        	System.out.println("vip 호출");
	}
	
	public int getAgentID() {
		return agentID;
	}

}

main 클래스

package inheritance;

public class CustomerTest {

	public static void main(String[] args) {
		
		Customer customerLee = new Customer();
		customerLee.setCustomerID(10100);
		customerLee.setCustomerName("Lee");
		
		Customer customerKim = new Customer();
		customerLee.setCustomerID(10101);
		customerLee.setCustomerName("Kim");
		
		VipCustomer vipCustomerOh = new VipCustomer();
		vipCustomerOh.setCustomerID(10102);
		vipCustomerOh.setCustomerName("oh");
		
		System.out.println(customerLee.showCustomerInfo());
		System.out.println(customerKim.showCustomerInfo());
	}

}

 


업캐스팅(상위클래스로의 묵시적 형변환)

상위 클래스 형으로 변수를 선언하고, 하위 클래스 인스턴스를 생성할 수 있다. 이는 하위클래스의 경우 상위클래스의 타입을 내포하고 있어서, 상위클래스로 묵시적으로 형변환이 가능한 것이다. 이러한 묵시적 형변환을 업캐스팅이라 한다. 단 상위클래스에 존재하지 않는 하위클래스에서 새롭게 선언된 부분은 접근할 수 없게 된다.

Customer VC = new VipCustomer();   // 상위클래스형인 Customer로 선언 후 하위 클래스인 VipCustomer()로 값을 초기화하였다.

* 상속관계에서 하위클래스를 인스턴스화하고 부모클래스로 업캐스팅했을 때 호출가능한 메서드는 부모클래스의 메서드만 가능하다. ( 자식객체가 가지고 있는 메서드를 사용하려면 다운캐스팅하여야 한다. ) 상위클래스의 메서드를 하위클래스에서 오버라이딩한 메서드가 있는 경우에는 업캐스팅한 경우일지라도 하위클래스의 메서드가 사용된다.

다운캐스팅

업캐스팅과는 반대로 업캐스팅된 자료형을 다시 하위클래스 본래의 자료형으로 형변환 하는 것을 의미한다. 단순히 상위클래스의 자료형을 하위클래스의 자료형으로 형변환이 아닌 업캐스팅된 자료형을 다시 하위클래스 본래의 자료형으로 형변환한다는 것에 주의해야 한다.

** instanceof 객체가 특정 클래스의 타입이 맞는지 아닌지 확인할 수 있다. [인스턴스] instanceof [확인하려는 클래스형] 

아래와 같이 instanceof를 통해 타입을 확인하여 사용할 수 있다.

package inheritance;

import java.util.ArrayList;

class Animal {
	public void move() {
		System.out.println("동물이 움직입니다.");
	}
}

class Human extends Animal {
	public void move() {
		System.out.println("사람이 걷습니다.");
	}
	public void readBook() {
		System.out.println("책을 읽는다.");
	}
}

class Tiger extends Animal {
	public void move() {
		System.out.println("호랑이가 네발로 뜁니다.");
	}
	public void hunting() {
		System.out.println("사냥을 한.");
	}
	
}

class Eagle extends Animal {
	public void move() {
		System.out.println("독수리가 하늘을 납니다..");
	}
	public void flying () {
		System.out.println("하늘을 난다.");
	}
}

public class AnimalTest {

	public static void main(String[] args) {
		AnimalTest test = new AnimalTest();
		test.moveAnimal(new Human());
		test.moveAnimal(new Tiger());
		test.moveAnimal(new Eagle());
		
		Animal[] animalList = new Animal[3];
		animalList[0] = new Human();
		
		ArrayList<Animal> animals = new ArrayList<Animal>();
		animals.add(new Tiger());
		
	}
	
	
	public void moveAnimal (Animal animal) {
		animal.move();
		if (animal instanceof Tiger) {
			//((Tiger) animal).hunting();
			Tiger tiger = (Tiger) animal;
			tiger.hunting();
		} else if (animal instanceof Human) {
			((Human) animal).readBook();
		} else if (animal instanceof Eagle) {
			Eagle eagle = (Eagle) animal;
			eagle.flying();
		}
		
	}

}

가상메서드

인스턴스의 변수나 메서드의 참조는 정의된 타입을 참조하지만, 가상메서드의 경우 실제 인스턴스의 메서드를 호출한다.

 


오버라이딩

상위클래스에서 선언된 메서드드를 하위클래스에서 재정의하는 것을 말한다.

** 오버로딩은 이름이 똑같은 메서드가 여러개 있는 것을 의미하고 오버로딩의 경우 메서드명만 같고 그 외 인자타입과 갯수는 달라야 한다. , 오버라이딩은 상위클래스에서 정의된 메서드명과 동일한 메서드명으로 하위클래스에서 생성하였을 때 메서드가 재정의되는 것을 의미한다.

오버로딩은 정적바인딩으로 컴파일 시에 중복된 메서드 중 호출되는 메서드가 결정된다.
오버라이딩은 동적바인딩으로 실행 시점에 오버라이딩 된 메서드를 찾아 호출한다.

오버라이딩의 조건

1. 메서드명, 메서드 인자 타입과 갯수, 리턴 타입 모두 동일해야 오버라이딩이 가능하다.

2. 상위클래스에서 정의된 메서드의 접근제어자가 하위클래스의 접근제어자 지정 범위보다 좁아져서는 안된다.  예를 들어 상위클래스에서 protected로 지정된 메서드를 하위클래스에서 오버라이딩할때 접근제어자를 public으로 지정할 수 없다.          ** public > protected > private 

3. private, final 메서드는 오버라이딩 할 수 없다. 

** final 키워드
https://advenoh.tistory.com/13
https://coding-factory.tistory.com/525

ex) DObject 라는 클래스를 각각 Line, Rect, Triangle 클래스가 상속받았다.

package inheritance;

public class DObject {
	public DObject next;
	public DObject () {
		next = null;
	} 
	public void draw () {
		System.out.println("original draw");
	}
}

class Line extends DObject{
	public void draw() {
		System.out.println("line");
	}
}

class Rect extends DObject{
	public void draw() {
		System.out.println("rect");
	}
}

class Triangle extends DObject{
	public void draw() {
		System.out.println("triangle");
	}
}
package inheritance;

public class DObjectTest {

	public static void main(String[] args) {
		DObject obj = new DObject();
		obj.draw();
		
		DObject line = new Line();
		line.draw();
		
		DObject rect = new Rect();
		rect.draw();
		
		DObject triangle = new Triangle();
		triangle.draw();
		
	}

}

위의 코드를 실행시 아래와 같이 결과값이 출력된다. 이는 상위클래스인 DObject 클래스의 draw메서드가 각각의 하위클래서에서 재정의되었기 때문이다.

 

 


다형성 (polymorphism)

하나의 객체가 여러가지 타입을 가질 수 있는 것을 의미한다. 하나의 코드로 다양한 구현이 가능하다. 하나의 클래스를 상속받은 다양한 클래스가 있는 경우 각 하위클래스에 메서드를 재정의하고, 상위클래스 타입으로 선언된 인자를 갖는 메서드 생성시, 해당 메서드에 넣은 인자에 따라 다양한 구현이 가능하다.

즉 다형성 구현은 상속, 재정의 메서드, 객체간의 형변환 세 가지의 조건이 필요하다.

package inheritance;

class Animal {
	public void move() {
		System.out.println("동물이 움직입니다.");
	}
}

class Human extends Animal {
	public void move() {
		System.out.println("사람이 걷습니다.");
	}
}

class Tiger extends Animal {
	public void move() {
		System.out.println("호랑이가 네발로 뜁니다.");
	}
}

class Eagle extends Animal {
	public void move() {
		System.out.println("독수리가 하늘을 납니다..");
	}
}

public class AnimalTest {

	public static void main(String[] args) {
		AnimalTest test = new AnimalTest();
		test.moveAnimal(new Human());
		test.moveAnimal(new Tiger());
		test.moveAnimal(new Eagle());
		
	}
	
	public void moveAnimal (Animal animal) {
		animal.move();
		
	}

}

ex) 다형성은 이형집합을 통한 구현과, 파라미터에 타입을 보내주어 구현하는 두 가지 방식이 존재한다.

public class InheritanceAssist {
    public static void main(String[] args){
        Shape[] shapes = new Shape[3];
        shapes[0] = new Rectangle();
        shapes[1] = new Line();
        shapes[2] = new Ellipse();

        for(Shape shape : shapes) {
            shape.draw();
        }
        System.out.println("========================");
        drawShapes(new Ellipse());
        drawShapes(new Line());
        drawShapes(new Rectangle());
    }

    public static void drawShapes(Shape shape) {
        shape.draw();
    }
}

 

 

 

출처

https://www.inflearn.com/course/%EC%9E%90%EB%B0%94-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%9E%85%EB%AC%B8#curriculum

https://dis.dankook.ac.kr/lectures/java20/wp-content/uploads/sites/73/2020/04/java1-lecture5.pdf

https://www.youtube.com/watch?v=2-jsaoj1MSk&list=PLOSNUO27qFbtjCw-YHcmtfZAkE79HZSOO&index=19 

반응형