본문 바로가기
Java/Java

상속) super 키워드, super() 메소드, protected 접근 제한자

by 박채니 2022. 3. 22.
SMALL

안녕하세요, 코린이의 코딩 학습기 채니 입니다.

 

개인 포스팅용으로 내용에 오류 및 잘못된 정보가 있을 수 있습니다.


super 키워드와 super() 메소드에 대해 알아보기 전, 상속을 하는 이유에 대해서 먼저 짚어보겠습니다.

 

데스크탑, 스마트폰, tv에 대한 정보를 갖고 있는 VO class를 만들어보았습니다.

 

Desktop class

public class Desktop {
	private String brand;
	private String productCode;
	private String productName;
	private int price;

	private String os;
	
	private String monitor;
	private String keyboard;
	private String mouse;
	
	public Desktop() {}
	public Desktop(String brand, String productCode, String productName, int price, String os, String monitor,
			String keyBoard, String mouse) {
		this.brand = brand;
		this.productCode = productCode;
		this.productName = productName;
		this.price = price;
		this.os = os;
		this.monitor = monitor;
		this.keyboard = keyBoard;
		this.mouse = mouse;
	}
	
	public String getBrand() {
		return brand;
	}
	public void setBrand(String brand) {
		this.brand = brand;
	}
	public String getProductCode() {
		return productCode;
	}
	public void setProductCode(String productCode) {
		this.productCode = productCode;
	}
	public String getProductName() {
		return productName;
	}
	public void setProductName(String productName) {
		this.productName = productName;
	}
	public int getPrice() {
		return price;
	}
	public void setPrice(int price) {
		this.price = price;
	}
	public String getOs() {
		return os;
	}
	public void setOs(String os) {
		this.os = os;
	}
	public String getMonitor() {
		return monitor;
	}
	public void setMonitor(String monitor) {
		this.monitor = monitor;
	}
	public String getKeyBoard() {
		return keyboard;
	}
	public void setKeyBoard(String keyBoard) {
		this.keyboard = keyBoard;
	}
	public String getMouse() {
		return mouse;
	}
	public void setMouse(String mouse) {
		this.mouse = mouse;
	}
	
	public String getDesktopInfo() {
		return "Desktop [" + brand + ", " + productCode + ", " + productName + ", " + price + ", "
				+ os + ", " + monitor + ", " + keyboard + ", " + mouse + "]";
				
	}
}

 

SmartPhone class

public class SmartPhone {
	
	private String brand;
	private String productCode;
	private String productName;
	private int price;
	
	private String os;
	
	private String carrier;	//통신사 정보

	public SmartPhone() {}
	public SmartPhone(String brand, String productCode, String productName, int price, String os, String carrier) {
		this.brand = brand;
		this.productCode = productCode;
		this.productName = productName;
		this.price = price;
		this.os = os;
		this.carrier = carrier;
	}
	
	public String getBrand() {
		return brand;
	}
	public void setBrand(String brand) {
		this.brand = brand;
	}
	public String getProductCode() {
		return productCode;
	}
	public void setProductCode(String productCode) {
		this.productCode = productCode;
	}
	public String getProductName() {
		return productName;
	}
	public void setProductName(String productName) {
		this.productName = productName;
	}
	public int getPrice() {
		return price;
	}
	public void setPrice(int price) {
		this.price = price;
	}
	public String getOs() {
		return os;
	}
	public void setOs(String os) {
		this.os = os;
	}
	public String getCarrier() {
		return carrier;
	}
	public void setCarrier(String carrier) {
		this.carrier = carrier;
	}
	
	public String getSmartPhoneInfo() {
		return "SmartPhone [" + brand + ", " +  productCode + ", " + productName + ", " + price + ", "
				+ os + ", " + carrier + "]";
	}
}

 

Tv class

public class Tv {
	private String brand;
	private String productCode;
	private String productName;
	private int price;
	
	private String resolution;	//화질
	private int size;
	
	public Tv() {}

	public Tv(String brand, String productCode, String productName, int price, String resolution, int size) {
		this.brand = brand;
		this.productCode = productCode;
		this.productName = productName;
		this.price = price;
		this.resolution = resolution;
		this.size = size;
	}

	public String getBrand() {
		return brand;
	}
	public void setBrand(String brand) {
		this.brand = brand;
	}
	public String getProductCode() {
		return productCode;
	}
	public void setProductCode(String productCode) {
		this.productCode = productCode;
	}
	public String getProductName() {
		return productName;
	}
	public void setProductName(String productName) {
		this.productName = productName;
	}
	public int getPrice() {
		return price;
	}
	public void setPrice(int price) {
		this.price = price;
	}
	public String getResolution() {
		return resolution;
	}
	public void setResolution(String resolution) {
		this.resolution = resolution;
	}
	public int getSize() {
		return size;
	}
	public void setSize(int size) {
		this.size = size;
	}
	
	public String getTvInfo() {
		return "Tv [" + brand + ", " + productCode + ", " + productName + ", " + price + ", "
				+ resolution + ", " + size + "]";
	}
}

 

ProductMain class

public class ProductMain {

	public static void main(String[] args) {
		Desktop desktop = new Desktop("삼성", "samsung-1234", "삼성점보데스크탑", 1_000_000, "Windows11", "좋은모니터", "좋은키보드", "좋은마우스");
		
		SmartPhone smartPhone = new SmartPhone("아이폰", "iphone-11-pro", "아이폰11프로", 800_000, "애플", "SKT");
		
		Tv tv = new Tv("LG", "lg-9898", "울트라와이드샤프TV", 5_000_000, "UHD", 80);
		
		System.out.println(desktop.getDesktopInfo());
		System.out.println(smartPhone.getSmartPhoneInfo());
		System.out.println(tv.getTvInfo());
	}
}

@콘솔출력값
Desktop [삼성, samsung-1234, 삼성점보데스크탑, 1000000, Windows11, 좋은모니터, 좋은키보드, 좋은마우스]
SmartPhone [아이폰, iphone-11-pro, 아이폰11프로, 800000, 애플, SKT]
Tv [LG, lg-9898, 울트라와이드샤프TV, 5000000, UHD, 80]

 

위 코드들을 보면 정말 거슬리는 한 가지가 있습니다.

바로 중복인데요, Desktop/SmartPhone/Tv class 모두 공통적인 필드를 갖고 있어 계속하여 중복이 발생하였습니다.

 

이럴 때 중복되는 코드들을 하나의 class에 정의하여 상속한다면, 코드의 중복이 없어지겠죠?

부모 클래스가 될 Product class를 생성해보겠습니다.

public class Product {
	private String brand;
	private String productCode;
	private String productName;
	private int price;
	
	public Product() {}
	public Product(String brand, String productCode, String productName, int price) {
		super();
		this.brand = brand;
		this.productCode = productCode;
		this.productName = productName;
		this.price = price;
	}
	
	public String getBrand() {
		return brand;
	}
	public void setBrand(String brand) {
		this.brand = brand;
	}
	public String getProductCode() {
		return productCode;
	}
	public void setProductCode(String productCode) {
		this.productCode = productCode;
	}
	public String getProductName() {
		return productName;
	}
	public void setProductName(String productName) {
		this.productName = productName;
	}
	public int getPrice() {
		return price;
	}
	public void setPrice(int price) {
		this.price = price;
	}
	
	public String getProductInfo() {
		return brand + ", " + productCode + ", " + productName + ", " + price;
	}
	
}

공통된 코드들을 Product class로 만들고, Desktop/SmartPhone/Tv 클래스가 상속 받게 하였습니다.

 

자식 클래스인 Desktop 클래스를 대표적인 예로 확인해보겠습니다.

public class Desktop extends Product {

	private String os;
	
	private String monitor;
	private String keyboard;
	private String mouse;
	
	public Desktop() {}
	public Desktop(String brand, String productCode, String productName, int price, String os, String monitor,
			String keyBoard, String mouse) {
		this.brand = brand;
		this.productCode = productCode;
		this.productName = productName;
		this.price = price;
		this.os = os;
		this.monitor = monitor;
		this.keyboard = keyBoard;
		this.mouse = mouse;
	}
	
	public String getOs() {
		return os;
	}
	public void setOs(String os) {
		this.os = os;
	}
	public String getMonitor() {
		return monitor;
	}
	public void setMonitor(String monitor) {
		this.monitor = monitor;
	}
	public String getKeyBoard() {
		return keyboard;
	}
	public void setKeyBoard(String keyBoard) {
		this.keyboard = keyBoard;
	}
	public String getMouse() {
		return mouse;
	}
	public void setMouse(String mouse) {
		this.mouse = mouse;
	}
	
	public String getDesktopInfo() {
		return "Desktop [" + brand + ", " + productCode + ", " + productName + ", " + price + ", "
				+ os + ", " + monitor + ", " + keyboard + ", " + mouse + "]";		
	}
}

extends 키워드를 이용하여 Product 클래스를 상속 받게 하였습니다만,

생성자와 메소드 등의 brand, productCode, productName, price는 private 접근 제어자이므로 상속은 받았지만 직접 접근이 불가한 상황으로 에러가 발생하였습니다.

 

이 때, 부모 클래스의 brand, productCode, productName, price를 가져와서 사용하려면 어떻게 해야할까요?


☞ super() 메소드

- 부모 클래스의 생성자 호출

- 생성자의 내부에서만 사용 가능하며 반드시 첫 줄에 단 한 번만 사용

public Desktop(String brand, String productCode, String productName, int price, String os, 
				String monitor,String keyBoard, String mouse) {
		super(brand, productCode, productName, price);
		this.os = os;
		this.monitor = monitor;
		this.keyboard = keyBoard;
		this.mouse = mouse;
	}

super() 메소드를 이용해 부모 클래스의 생성자를 호출하여 필드 값에 넘어온 매개인자를 대입할 수 있었습니다.

 

그렇다면 기본 생성자는 어떻게 될까?

	public Desktop() {
		//super(); 생략
	}

언뜻 보기엔, 아무것도 입력이 안되어있는 것 같아보이지만 super()가 생략이 되어있는 상태입니다.

모든 생성자는 첫 줄에 반드시 this() 또는 super()가 있어야합니다. 만일 아무것도 써주지 않으면 JVM은 super()를 자동 삽입합니다.

 

즉 생성자를 호출할 때 반드시 부모 클래스의 생성자가 호출이 된다는 뜻이고,

이러한 이유로 인하여 자식 생성자로 객체를 생성할 때 부모 클래스의 객체가 만들어지는 것입니다.

 

☞ super. 키워드

- 부모의 객체를 가리키는 것

- 필드명의 중복 또는 메서드 오버라이딩으로 가려진 부모의 필드 또는 메소드를 호출하기 위해 사용

public String getDesktopInfo() {
		return "Desktop [" + super.getProductInfo() + ", "
				+ os + ", " + monitor + ", " + keyboard + ", " + mouse + "]";		
	}

사실 getDesktopInfo()는 오버라이딩이 된 메소드가 아니기도 하고, 애초에 부모클래스의 getProductInfo()가 public 메소드이기 때문에 this. 으로도 접근이 가능합니다.

 

그렇다면 만일 각 클래스 별 Info 메소드가 아닌 Object가 제공하는 toString 메소드를 오버라이딩해서 사용하고 있다면 어떻게 될까요?

//부모 클래스 Product
public class Product {
	private String brand;
	private String productCode;
	private String productName;
	private int price;
	
	public Product() {}
	public Product(String brand, String productCode, String productName, int price) {
		this.brand = brand;
		this.productCode = productCode;
		this.productName = productName;
		this.price = price;
	}
	
	//get,set 생략
	
	@Override
	public String toString() {
		return brand + ", " + productCode + ", " + productName + ", " + price;
	}
	
	public String getProductInfo() {
		return brand + ", " + productCode + ", " + productName + ", " + price;
	}
}

 

//자식 클래스 Desktop
public class Desktop extends Product {

	private String os;
	
	private String monitor;
	private String keyboard;
	private String mouse;
	
	
	public Desktop() {
		super();
	}
	public Desktop(String brand, String productCode, String productName, int price, String os, String monitor,
			String keyBoard, String mouse) {
		super(brand, productCode, productName, price);
		this.os = os;
		this.monitor = monitor;
		this.keyboard = keyBoard;
		this.mouse = mouse;
	}
	
	//get,set 생략
	
	@Override
	public String toString() {
		return "Desktop [" + super.toString() + ", "
				+ os + ", " + monitor + ", " + keyboard + ", " + mouse + "]";
	}
	
	public String getDesktopInfo() {
		return "Desktop [" + super.getProductInfo() + ", "
				+ os + ", " + monitor + ", " + keyboard + ", " + mouse + "]";		
	}
}

 

public class ProductMain {

	public static void main(String[] args) {
		Desktop desktop = new Desktop("삼성", "samsung-1234", "삼성점보데스크탑", 1_000_000, 
        					"Windows11", "좋은모니터", "좋은키보드", "좋은마우스");
		
		System.out.println(desktop.toString());
	}
}

이렇게 오버라이딩된 메소드를 사용할 때, 그리고 부모 클래스의 메소드를 가져와야할 때는 반드시 super. 키워드를 사용해야 합니다.

만일 자식 클래스의 toString 메소드에서 super. 이 아닌 this. 을 하게 된다면 계속해서 자신의 메소드를 불러오는 것이기 때문에 StackOverFlow에러가 발생하게 됩니다. 

 

이렇게 중복 제거와 super키워드와 super() 메소드를 통해 부모의 생성자, 부모의 메소드에 접근할 수 있었습니다.

 

☞ protecte 접근제한자

만일 super키워드, super()메소드를 사용하기 싫다! 나는 부모 필드에 직접 접근하고 싶다! 라고 한다면,

부모의 필드는 protected로 지정하면 되겠죠?


protected 접근 제한자 : 동일 패키지의 모든 클래스 + 다른 패키지의 자식 클래스에서 사용 가능

//부모 클래스 Product 
public class Product {
	protected String brand;
	protected String productCode;
	protected String productName;
	protected int price;
    
    //... 이하 생략
    
}

 

//자식 클래스 Desktop
public class Desktop extends Product {
	
    //... 이하 생략

	public Desktop(String brand, String productCode, String productName, int price, String os, String monitor,
			String keyBoard, String mouse) {
//		super(brand, productCode, productName, price);
		this.brand = brand;
		this.productCode = productCode;
		this.productName = productName;
		this.price = price;
		this.os = os;
		this.monitor = monitor;
		this.keyboard = keyBoard;
		this.mouse = mouse;
	}
	
	public String getDesktopInfo() {
		return "Desktop [" + brand + ", " + productCode + ", " + productName + ", " + price + ", "
				+ os + ", " + monitor + ", " + keyboard + ", " + mouse + "]";		
	}
}
//Main 메소드
public class ProductMain {

	public static void main(String[] args) {
		Desktop desktop = new Desktop("삼성", "samsung-1234", "삼성점보데스크탑", 1_000_000, "Windows11", "좋은모니터", "좋은키보드", "좋은마우스");
		
		System.out.println(desktop.getDesktopInfo());
	}
}

@콘솔출력값
Desktop [삼성, samsung-1234, 삼성점보데스크탑, 1000000, Windows11, 좋은모니터, 좋은키보드, 좋은마우스]

이렇게 부모 필드의 직접 접근하여 값 대입을 하였고, 직접 접근이 가능하므로 get메소드를 통해 필드 값을 가져오는 게 아닌 직접 필드를 호출하여 메소드 출력을 할 수 있었습니다.

LIST