고래씌

[JAVA] 9. 상속 & 오버라이딩 본문

JAVA/JAVA 기초

[JAVA] 9. 상속 & 오버라이딩

고래씌 2023. 10. 13. 16:58

1. 상속

1) 상속을 쓰는 이유?

☞ 매 클래스마다 중복된 코드들을 일일이 기술해두게 되면 수정, 추가, 삭제시 매번 일일이 찾아가서 수정을 해야하는 번거로움이 생긴다.
    ex) 필드명 수정, 새로운 필드가 추가시 

 
☞ 상속이라는 개념을 적용시켜서 매 클래스마다 "중복된" 필드, 메소드들을 단 한번 또 하나의 클래스에 정의해둔뒤, 해당 클래스를 상속받아서 부모클래스의 필드와 메소드를 가져다 쓰는 방식으로 코드의 중복을 피해야한다.
 
super();  => 자기보다 상위의 존재를 말함. 즉, 부모클래스의 생성자 호출을 의미!
               => 부모클래스의 주소값을 보관하고 있음.
               => super으로 부모에 접근이 가능
 

☞ super(brand, pCode, pName, price) 로 작성하여도 되고, 
     super.setBrrand(brand);
     super.setpCode(pCode);
     super.setpName(pName);
     super.setPrice(price); 으로 작성하여도 된다

=> super는 보이지 않지만 생성된 Product의 주소값을 보관하고 있음!
 

2) 상속의 장점

▶ 보다 적은 양의 코드로 새로운 클래스들을 작성 가능
▶ 중복된 코드를 공통적으로 관리하기 때문에 새로운 코드를 추가하거나 수정할 때 용이함
    => 프로그램의 생산성 증대 및 유지보수에 크게 기여한다.

3) 상속의 특징

클래스간의 상속은 다중상속이 불가능함(단일 상속만 가능)
▶ 부모의 private 멤버는 상속은 되지만 직접 접근 불가. 자식 생성자 안에서 부모의 private 필드에 직접 접근하여 대입 불가능하다! super() 이용하여 부모 생성자쪽으로 넘겨 생성하거나 setter, getter 메소드를 이용하여 접근
▶ 명시되어있지 않지만 모든클래스는 Object클래스의 후손
     => Object클래스에 있는 메소드를 모든 클래스에서 사용이 가능함.
     => Object클래스에 있는 메소드가 마음에 안들면 오버라이딩을 통해 재정의 가
우리 눈에 보이진 않지만 우리는 계속 public class Product extends Object 를 사용하고 있었다!

 

2. 오버라이딩

: 부모클래스에 이미 존재하는 메소드를 재정의(override)하는 것


☞ 실행하고자 하는 메소드가 자식클래스에 없다면, 자동으로 부모클래스에 있는 메소드가 실행됨.
단, 자식클래스에서 재정의된(오버라이딩된) 메소드가 있을 경우 자식클래스에 있는 메소드가 실행에서 ★우선권을 가진다.
 
<오버라이딩>
- 상속받고 있는 부모클래스의 메소드를 자식 클래스에서 재정의(재작성)하는 것
- 부모가 제공하고 있는 메소드를 자식이 일부 고쳐서 사용하겠다는 의미.(실행 우선권은 자식메소드에게 있다)

1) ★오버라이딩 성립조건
- 부모메소드의 메소드명과 동일해야함
- 매개변수 자료형, 개수, 순서가 정확하게 일치해야함
- 반환형이 일치해야함
- 부모메소드의 접근제한자보다 같거나 공유범위가 더 커야한다.
- private 메소드, final 메소드 오버라이딩 불가

2) @Override 어노테이션
- 생략가능(명시를 안해도 부모메소드와 형태가 같으면 오버라이딩이 된 것)
- 그럼에도 불구하고 어노테이션을 붙이는 이유?
  → 잘못 기술했을 경우(성립조건과 다르게)오류를 알려주기 때문에 다시한번 검토할 수 있게 유도한다.
   혹시라도 부모메소드가 나중에 수정이 되었을 경우 오류를 알려주기 때문에 검토할 수 있게 유도한다.
   이 메소드가 오버라이딩된 메소드라는걸 알리고자 하는 목적이 큼.
 
3) 주의 사항
- 메소드의 리턴타입은 오버로딩 조건과 관계 없음. 여기서 <- 오버라이딩과 오버로딩의 차이점!
 

▶ 출력문 안에 참조형변수(레퍼런스 변수)를 제시해서 출력하고자 할 때, 내부적으로 JVM이 자동으로 toString()메소드를 호출한다.

부모클래스인 Object클래스에 있는 toString()
해당 참조형의 풀클래스명 + @ + 해당객체의 주소값

자식클래스인 Book클래스에서 toString()메소드를 재정의
해당 객체의 모든 필드값을 하나의 문자열로 합쳐서 돌려줄 수 있다.

모든 클래스는 Object클래스의 후손이다. 즉 항상 최상위클래스는 Object
=> 즉, Object에 있는 모든 메소드들을 다 가져다 쓸 수 있음. 마음에 들지 않으면 재정의가 가능함.


메소드 오버라이딩 전 : 
 com.kh.chap03_override.model.vo.Book@16진수 주소값
메소드 오버라이딩 후 :
책 제목 : 수학의 정석, 책 저자 : 김길동, 15000

 
 
[Tv클래스 information() 메소드 주석]

☞ 부모클래스 Product 에 있는 informaion 메소드가 호출되어 출력된 것을 확인
 
 
[Product 클래스 - 부모클래스]

package com.kh.chap01_beforeVsAfter.after.model.vo;

public class Product {
	
	// 세 클래스 모두 공통적으로 기술되어있던 요소들만 추출해서 정의해둔 클래스
	private String brand;
	private String pCode;
	private String pName;
	private int price;
	
	public Product() {
		
	}

	public Product(String brand, String pCode, String pName, int price) {
		super();
		this.brand = brand;
		this.pCode = pCode;
		this.pName = pName;
		this.price = price;
	}

		
	// 세 클래스 모두 공통적으로 기술되어있던 메소드
	public String getBrand() {
		return brand;
	}

	public void setBrand(String brand) {
		this.brand = brand;
	}

	public String getpCode() {
		return pCode;
	}

	public void setpCode(String pCode) {
		this.pCode = pCode;
	}

	public String getpName() {
		return pName;
	}

	public void setpName(String pName) {
		this.pName = pName;
	}

	public int getPrice() {
		return price;
	}

	public void setPrice(int price) {
		this.price = price;
	}
	
	public String information() {
		return "brand : " + brand + " pCode : " + pCode + " pName : " + pName + " price : " + price;
	}
}

[Desktop - 자식클래스]

package com.kh.chap01_beforeVsAfter.after.model.vo;

             // 자식           부모
             // 후손           조상
             // 하위           상위
public class Desktop extends Product{
	
	private boolean allInOne;

	public Desktop() {
//		super();   // 자기보다 상위의 존재를 말함. 즉 부모클래스의 생성자 호출을 의미
		// super. 으로 부모에 접근이 가능
				
	}
	
	public Desktop(String brand, String pCode, String pName, int price, boolean allInOne) {
		
		// brand, pCode, pName, price를 부모클래스의 Product에 있는 필드에 초기화
		// Product(String brand, String pCode, String pName, int price) 생성자 호출
//		super(brand, pCode, pName, price);  
		
		// 아래에 이런 방법으로도 초기화가 가능하다!
		super.setBrand(brand);
		super.setpCode(pCode);
		super.setpName(pName);
		super.setPrice(price);
		
		this.allInOne = allInOne;
	}
	
	
	public boolean isAllInOne() {
		return allInOne;
	}

	public void setAllInOne(boolean allInOne) {
		this.allInOne = allInOne;
	}

	// 오버라이딩 : 부모클래스에 이미 존재하는 메소드를 재정의(override)하는 것
	public String information() {
		return super.information() + " allInOne : " + allInOne;
	}
}

[SmartPhone - 자식클래스]

package com.kh.chap01_beforeVsAfter.after.model.vo;

public class SmartPhone extends Product{
	
	private String mobileAgency;
	
	public SmartPhone() {
		
	}
	
	public SmartPhone(String brand, String pCode, String pName, int price, String mobileAgency) {
		super(brand, pCode, pName, price);
		this.mobileAgency = mobileAgency;
	}
	

	public String getMobileAgency() {
		return mobileAgency;
	}

	public void setMobileAgency(String mobileAgency) {
		this.mobileAgency = mobileAgency;
	}

	
	public String information() {
		return super.information() + " moblieAgency : " + mobileAgency;
	}
}

[Tv - 자식클래스]

package com.kh.chap01_beforeVsAfter.after.model.vo;

public class Tv extends Product{
	
	private int inch;
	
	public Tv() {
		
	}
	
	public Tv(String brand, String pCode, String pName, int price, int inch) {
		super(brand,pCode, pName, price);
		this.inch = inch;
	}


	public int getInch() {
		return inch;
	}

	public void setInch(int inch) {
		this.inch = inch;
	}
	
//	public String information() {
//		return super.information() + " inch : " + inch;
//	}
}

[Run 클래스]

package com.kh.chap01_beforeVsAfter.run;

import com.kh.chap01_beforeVsAfter.after.model.vo.*;

public class Run {

	public static void main(String[] args) {
		
		Desktop d = new Desktop("삼성", "d-01", "삼성데스크탑", 1_500_000, true);
		
		SmartPhone sm = new SmartPhone("삼성", "s-01", "z-플립5", 1_500_000, "kt");
		
		Tv t = new Tv("엘지", "t-01", "얇은 티비", 3_500_000, 60);
		
		
		/*
		 * 실행하고자 하는 메소드가 자식클래스에 없다면, 자동으로 부모클래스에 있는 메소드가 실행됨.
		 * 단, 자식클래스에서 재정의된(오버라이딩된) 메소드가 있을 경우 자식클래스에 있는 메소드가
		 * 실행에서 우선권을 가진다.
		 */
		
		System.out.println(d.information());
		System.out.println(sm.information());
		System.out.println(t.information());   // Tv 클래스는 information 메소드를 주석하고
		                                       // 출력하면 부모클래스에 있는 information 메소드를 출력함
		
	}

}