[Java] 상속과 다형성

자바 클래스를 만들 때, 클래스 간의 관계를 판단해보자.

  • is a 관계
  • has a 관계

🚗 예1) 자동차 클래스 - 엔진 클래스, 바퀴 클래스 ...

  • '자동차 has a 엔진' 관계를 가지고 있다.
    : 엔진 클래스를 자동차 클래스의 멤버변수화 시키면 된다.

📱 예2) 휴대폰 클래스 - 아이폰 클래스, 안드로이드폰 클래스 ...

  • '아이폰 is a 휴대폰' 이라는 관계를 가지고 있다.
    : is a 관계를 가지고 있으면 상속 관계를 만들어서 상속의 이점을 누릴 수 있다.

1. 상속 (Inheritance)

  • '물려받는다'는 의미
  • B클래스가 A클래스를 상속받으면, B클래스는 A클래스가 가지고 있는 구성원(멤버변수, 메서드) 사용할 수 있음
  • ⚡ 자바에서는 모든 클래스가 다른 클래스를 상속받는데, 그 최초의 부모클래스는 Object 클래스이다 ⚡

1) 상속관계의 클래스 생성

문법:
class B extends A { ... }

고객 정보를 관리하는 'Customer' 클래스가 있을 때, 이 클래스의 구성요소를 모두 포함하면서 새로운 속성과 기능을 가진 'VIPCustomer' 클래스를 만들어보자.

(1) protected 접근제한자

  • 부모클래스의 멤버변수를 private으로 선언하면 자식클래스에서 접근 불가능하다.
  • 상속관계의 클래스에서는 접근할 수 있도록 protected를 사용한다.
    (접근제한자 - 이전 포스팅 참고)

<Customer.java>

public class Customer {
	protected int customerID;
	protected String customerName;
	protected String customerGrade;
	
}

<VIPCustomer.java>

public class VIPCustomer extends Customer {
	// 자식클래스인 VIPCustomer 클래스만의 멤버변수
        double saleRatio;  	
}

<CustomerTest.java>

public class CustomerTest {

	public static void main(String[] args) {
		VIPCustomer vc1 = new VIPCustomer();
        
    		// VIPCustomer 클래스는 customerName 멤버변수가 없지만,
        	// Customer 클래스로부터 상속받았기 때문에 사용 가능
		vc1.customerName = "홍길동";  
		System.out.println(vc1.customerName);  // 홍길동		
	}

}

(2) super()

  • 부모클래스의 참조값을 가지고 있는 예약어. 자식클래스에서 부모클래스로 접근할 때 사용
  • 명시하지 않아도 자식클래스 생성자에서 super()를 자동 호출함
  • super()를 호출하면 부모클래스의 디폴트 생성자가 호출됨
    = ⚡ 자식클래스의 객체가 만들어질때 무조건 부모클래스 객체가 먼저 만들어짐
    자식클래스가 부모클래스의 멤버변수와 메서드를 사용할 수 있는 이유!!

부모클래스인 Customer 클래스에 아래와 같이 생성자를 구현했을 때,

public class Customer {
	protected int customerID;
	protected String customerName;
	protected String customerGrade;
	
	// 생성자를 직접 구현 ( => 디폴트 생성자 안만들어짐)
	public Customer(int customerID, String customerName) {
		this.customerID = customerID;
		this.customerName = customerName;
		customerGrade = "Silver";
	}
	
}

VIPCustomer.java에서는 오류가 발생한다.
VIPCustomer 클래스의 생성자를 명시하지 않았기 때문에, 컴파일시에 디폴트생성자와 그 안에 super()가 자동 생성된다.

super()가 부모클래스의 디폴트생성자를 호출해야 하는데, Customer클래스에는 디폴트생성자가 없어 오류가 발생하는 것이다.

아래와 같이 VIPCustomer 클래스의 생성자와 super()를 명시해준다.

public class VIPCustomer extends Customer {
	double saleRatio;

	public VIPCustomer(int customerID, String customerName) {
		super(customerID, customerName);  // 부모클래스의 생성자 호출
		super.customerGrade = "VIP";  // 부모클래스의 멤버변수 참조
		saleRatio = 0.1;
	} 		

}

2) 메서드 오버라이딩 (Method Overriding)

  • 부모클래스의 메서드를 자식클래스에서 재정의
  • 메서드의 반환형, 이름, 매개변수 개수/자료형이 모두 같아야 함
    (그렇지 않으면 다른 메서드로 인식됨)

<Customer.java>

public class Customer {
	protected int customerID;
	protected String customerName;
	protected String customerGrade;
	
	public Customer(int customerID, String customerName) {
		this.customerID = customerID;
		this.customerName = customerName;
		customerGrade = "Silver";
	}
	
    	// 가격을 반환하는 메서드 
	public double calcPrice(double price) {
		return price;
	}
    	// VIP손님은 정가를 반환하지 않을 거라서 재정의 필요
	
}

<VIPCustomer.java>

public class VIPCustomer extends Customer {
	double saleRatio;

	public VIPCustomer(int customerID, String customerName) {
		super(customerID, customerName);
		super.customerGrade = "VIP";
		saleRatio = 0.1;
	}

	// 메소드 오버라이딩
	@Override
	public double calcPrice(double price) {
		return price * (1-saleRatio);
	} 		
	// VIPCustomer 클래스에 맞게 재정의해서 사용
}

2. 다형성 (Polymorphism)

  • 하나의 코드가 여러 자료형으로 구현되어 실행되는 것을 뜻함
  • 필요에 따라 상속받은 모든 클래스를 하나의 상위 클래스로 처리하거나, 다형성에 의해 각 자식클래스의 구현을 실행할 수 있음

1) 묵시적 클래스 형변환

  • 자식클래스로 객체를 생성할 때, 그 객체를 참조하는 변수의 자료형을 부모클래스로 형변환하여 사용할 수 있다.
    Customer vc = new VIPCustomer();
    • 덜 정밀한 자료형 → 더 정밀한 자료형으로의 형변환 = Upcasting
      (형변환 - 이전 포스팅 참고)
    • VIPCustomer는 Customer클래스가 가지고 있는 구성원들을 모두 가지고 있기 때문이다.
  • 선언한 클래스형에 기반하여 멤버변수/메서드에 접근할 수 있다.
public class CustomerTest {

	public static void main(String[] args) {
		Customer c = new Customer(10, "김철수");
		VIPCustomer v = new VIPCustomer(11, "홍길동");		
       		// 묵시적 형변환
		Customer vc = new VIPCustomer(12, "알파카"); 	
		
        	System.out.println(v.saleRatio);  // 0.1		
//		System.out.println(vc.saleRatio);   
//		vc는 Customer 클래스를 참조하는 변수이므로, saleRatio 변수에 접근 불가
	}

}

2) 가상메서드

  • 변수메서드가 사용하는 메모리는 다르다.
    • 멤버변수 : 인스턴스가 생성될 때마다 스택 메모리에 생성됨
    • 메서드 : 메모리상의 메서드 영역에 생성됨
      (멤버변수처럼 인스턴스가 생성될때마다 새로 생성되지 않음)
  • 메서드를 호출하면 메서드 영역의 주소를 참조하여 명령이 실행된다.
public class CustomerTest {

	public static void main(String[] args) {
		Customer c = new Customer(10, "김철수");
		VIPCustomer v = new VIPCustomer(11, "홍길동");			
		Customer vc = new VIPCustomer(12, "알파카"); 		
		
		System.out.println(c.calcPrice(1000));  // 1000.0
		System.out.println(v.calcPrice(1000));  // 900.0
		// 가상메서드 
		System.out.println(vc.calcPrice(1000));  // 900.0
	}

}
  • 선언한 클래스형이 아닌, 생성된 인스턴스의 메서드를 호출한다. ⚡

좋은 웹페이지 즐겨찾기