LECTURE/JAVA

08.상속

heywoo 2023. 1. 6. 22:32
상속이란?

- 부모클래스가 가지는 멤버를 자식클래스가 물려받아 자신의 멤버인 것처럼 사용할 수 있도록 만든 기술이다.

- 다만, 생성자는 상속받지 못하며, 부모가 가지고 있는 private 멤버는 접근할 수 없다.

(protected로 변경하면 상속 관계에 있기 때문에 호출 가능_

- 자바는 하나의 클래스만을 부모클래스로 가지는 단일 상속만 지원한다.

- 클래스 간의 상속 시에는 부모 클래스의 확장의 개념으로 extends 키워드를 사용한다. 

ex) public class Academy extends Company{}  → Academy 클래스는 Company 클래스를 상속한다.

- 추가적인 자신의 멤버 작성이 가능하며 메소드 재정의(overriding)라는 기술을 이용해서 부모가 가진 메소드를 재정의 하는 것도 가능하다.

 

상속의 장단점
  • 장점
    • 새로운 클래스를 작성 시 기존에 작성 된 클래스의 재사용 가능
    • 클래스간 계층 관계가 형성되어 다형성 문법의 토대가 됨 
  • 단점
    • 부모클래스의 기능 변경 시 자식클래스의 정상적인 동작 유무 예측이 힘듦
    • 반대로 자식클래스가 물려받아 사용하는 기능들을 부모클래스에서 변경 시 어려움이 따름
    • 부모클래스로부터 물려받은 기능이 자식클래스에서는 무의미 할 수 있음

→ 장단점을 고려해 볼 때 IS-A 관계로 구분되는 경우에만 사용해야 함

 

IS-A

“자식클래스는 (하나의)부모 클래스이다.”의 관계로, 부모클래스의 멤버들을 자식클래스가 상속 받음

▼Circle is a Shape. -> Circle 클래스는 하나의 Shape 클래스이다.

Circle 클래스는 Shape 클래스를 상속받아 넓이 구하기 메소드를 쓸 수 있다..

 

super와 super()
  • super

부모클래스의 인스턴스 주소를 보관하는 레퍼런스 변수이며, 자식클래스 내의 모든 생성자와 메소드 내에서 부모클래스의 레퍼런스 변수를 사용할 수 있음

  • super()

- 부모 생성자를 호출하는 구문으로 매개변수의 타입, 개수, 순서가 일치하는 부모의 생성자를 호출하게 됨(private 생성자를 제외)

- 모든 생성자의 맨 첫 줄에 부모의 기본 생성자를 호출하는 구문인 super(); 를 컴파일러가 자동으로 추가한다. 

- 명시적(부모 클래스에 기본 생성자가 없는 경우), 묵시적 모두 가능하다.

      (this()는 해당 클래스의 생성자를 호출하는 구문이다.)

 

오버라이딩(Overriding)
  • 정의

부모클래스에서 상속받은 메소드를 자식클래스가 재정의하여 사용하는 것

  • 성립조건
  1. 메소드 이름 동일
  2. 메소드 리턴 타입 동일
  3. 매개변수의 타입, 개수, 순서가 동일
  4. private 메소드는 오버라이딩 불가능
  5. final 키워드가 사용된 메소드는 오버라이딩 불가능
  6. 접근제한자는 부모 메소드와 같거나 더 넓은 범위여야 함
  7. 예외처리는 같은 예외이거나 더 구체적(하위)인 예외를 처리해야 함
  • @Override 어노테이션

    JDK 1.5부터 추가 된 문법으로 오버라이딩 성립 요건을 체크하여 성립 되지 않은 경우 컴파일 에러를 발생시킨다.

 

▼ 예시 : 부모 클래스 Product

public class Product {
	
	/* 필드(전역 변수) */
	private String code;			//상품코드
	private String brand;			//제조사
	private String name;			//상품명
	private int price;				//가격
	private Date manufacturingDate; //제조일자
	
	/* 기본 생성자 */
	public Product() {
		super();
		System.out.println("Product 클래스의 기본 생성자 호출");
	}

	/* 모든 필드를 초기화 하는 생성자 */
	public Product(String code, String brand, String name, int price, Date manufacturingDate) {
		super();
		this.code = code;
		this.brand = brand;
		this.name = name;
		this.price = price;
		this.manufacturingDate = manufacturingDate;
	}

	/* getters, setters 생략 */
    
    /* 모든 필드 값을 문자열로 반환하는 메소드 */
	public String getInformation() {
		return "Product [code=" + code + ", brand=" + brand + ", name=" + name + ", price="
				+ price + ", manufacturingDate=" + manufacturingDate + "]";
	}

 

▼ Product 를 상속받은 Computer 클래스

/* Computer 는 하나의 Product 이다. (IS-A) */
public class Computer extends Product {
	
	/* Computer만 가지는 추가적인 속성을 필드로 정의한다. */
	private String cpu;
	private int hdd;
	private int ram;
	private String operationSystem;
	
	/* 기본 생성자 */
	public Computer() {
		super();
	}

	/* 모든 필드를 초기화 하는 생성자 */
	public Computer(String cpu, int hdd, int ram, String operationSystem) {
		/* 부모 클래스의 기본 생성자 호출 */
		super();
		this.cpu = cpu;
		this.hdd = hdd;
		this.ram = ram;
		this.operationSystem = operationSystem;
	}

	/* 부모의 필드까지 모두 초기화 하는 생성자 */
	public Computer(String code, String brand, String name, int price, Date manufacturingDate,
			String cpu, int hdd, int ram, String operationSystem) {
		
		/* 부모 클래스의 모든 필드 초기화 매개변수 생성자 호출 */
		super(code, brand, name, price, manufacturingDate);
		
		this.cpu = cpu;
		this.hdd = hdd;
		this.ram = ram;
		this.operationSystem = operationSystem;
		
	}

	/* getter, setter 생략*/
	/* 부모 필드의 메소드에 대해서는 자신의 멤버처럼 사용 가능하므로 따로 작성할 필요가 없다.
	 * 자식 클래스에 추가 된 필드에 대해서만 작성한다.*/
     
     
     /* 모든 필드 값을 문자열로 반환하는 메소드 */
	
    @Override
	public String getInformation() {
		
		/* 부모 클래스에 작성한 getter를 이용해서 부모 필드가 가지고 있는 값도 한 번에 문자열 합치기
         
		 * 부모가 가진 멤버는 super. 와 this. 둘 다 사용이 가능하다.
		 * 하지만 코드의 의미를 명확히 하기 위해 super. 을 사용*/
		
		return "Computer ["
				+ "code=" + super.getCode() 
				+ ", brand=" + super.getBrand()
				+ ", name=" + super.getName()
				+ ", price=" + super.getPrice()
				+ ", manufaturingDate=" + super.getManufacturingDate()
				+ ", cpu=" + this.cpu
				+ ", hdd=" + this.hdd
				+ ", ram=" + this.ram
				+ ", operationSystem=" + this.operationSystem
				+ "]";
		
        
		/* super.getInformation() : 정상적으로 부모의 메소드를 호출한다.
		 * this.getInformation() : 재귀 호출(자기 자신의 메소드를 다시 호출하는 것)이 일어나며 StackOverflowError 발생한다.
		 * getInformation() : this. 이 자동으로 추가 되어 동일하게 재귀 호출이 일어난다. */
		return super.getInformation() 
				+ " Computer ["
				+ "cpu=" + this.cpu
				+ ", hdd=" + this.hdd
				+ ", ram=" + this.ram
				+ ", operationSystem=" + this.operationSystem
				+ "]";
	}