자바의 상속 - accidentlywoo/java GitHub Wiki

자바의 상속


  1. 상속의 개념을 이해하고, 상속과 생성자의 관계를 설명할 수 있다.
  2. 변수에서의 상속과 상속되지 않는 멤버 변수의 종류를 구분하고, 메서드의 Overloading과 Overriding을 구별할 수 있다.

상속(Inheritance)

ex) 사원 구현 사원인 동시에 관리자로서의 특징을 가지고 있음 Employee와 공통적인 특성은 Employee로부터 상속받음 Manager로서의 특성만 멤버 변수로 선언

상속을 통해 소스코드의 재사용성이 보장되며, 가독성이 높아짐

is a ~관계

새로운 클래스가 필요한데, 공통적인 특징을 가진 클래스가 이미 존재한다면?

존재하는 클래스를 상속하여 쉽게 구현 가능

공통된 기능을 가지고 있다고 해서 모두 상속할 수 없음 부모 클래스와 자식 클래스의 관계가 일반화와 특별화의 관계 'is a~'관계에 있어야 함

논리적으로 'is a~'관계가 불성립 != 상속관계

문법적으로 강제되는 것이 아니라 논리적으로 판단되어야 함

부적절한 상속은 소스코드에 대한 분석과 개발을 어렵게 만듦

예약어 extends

확장이라는 의미의 extends 예약어를 사용하는 이유는?

상속은 추가적인 것들을 확장한다는 의미가 중요함

상속

부모로부터 물려받은 공통적인 특징 + 자식 클래스에서만 가지는 추가적인 특징

부모의 기능을 확장했다는 의미의 extends 예약어를 사용하여 상속 표현

단일상속(Single inheritance)

개발의 편의성과 가독성을 위해 문법적으로 단일상속만을 허용함 단일상속이란 하나의 클래스는 오직 하나의 부모 클래스만 상속할 수 있다는 의미임

다중상속의 문제점은? -> 다중상속을 허용하면 중보되는 변수와 메서드가 상속되는 문제가 발생함 모든 클래스의 변수 이름을 다르게 선언하면 문제를 방지할 수 있을까? -> 클래스를 만들 때 중복 가능성을 모두 예측할 수 없기 때문에 해결 방법이 될 수 없음

자바에서는 문법적으로 단일상속만을 허용하여 이런 문제들을 근본적으로 차단함

생성자 자동 호출

상속된 부모 클래스 객체가 생성될 때 부모 클래스의 생성자도 자동으로 호출되어 수행되면서 객체 초기화가 이루어지도록 함

부모 클래스의 생성자 초기화 수행 --매개변수가 없는 기본 생성자 호출--> <-- 자동 호출-- 자식 클래스의 초기화가 이루어짐

부모 생성자 super()

  • this() 생성자 호출 클래스 안에서 Overloading 된 또 다른 생성자를 호출하기 위해 사용

  • super() 생성자 호출 부모 클래스의 생성자를 명시적으로 호출할 때 사용 부모 클래스의 생성자가 Overloading되어 여러 개 존재하는 경우 특정 생성자를 호출하기 위해 사용

class Shape {
	int x = 0;
	int y = 0;
	
	Shape() {
		this(0, 0);
	}
	
	Shape(int x, int y){
		this.x = x;
		this.y = y;
	}
}

class Circle extends Shape {
	int radius;
	Circle(int x, int y, int radius){
		super(x, y);// 명시적 호출
		//
		this.radius = radius;
	}
	
	public void draw() {
		System.out.println("원그리기"+x+","+y+","+radius);
	}
}

부모 클래스의 생성자가 항상 자식 클래스의 생성자보다 먼저 수행되어야 함 -> 순서가 바뀌면 컴파일이 되지 않는다.

상속과 멤버

변수의 상속

부모 클래스의 멤버 변수는 자동으로 자식 클래스에 상속되어 들어감 부모 클래스가 가진 변수와 같은 이름의 변수를 선언하면, 부모 클래스의 변수는 상속되지 않음 -> 자식 클래스에서 정의한 변수가 사용됨

동일한 이름으로 자식 클래스에 정의된 변수는 상속에서 제외됨

super 예약어

  • this 예약어 생성된 객체 자신에 대한 참조를 의미함 멤버 변수와 메서드 매개변수의 이름이 같을 경우, 두 변수를 구분하기 위해 사용함

  • super 예약어 부모 객체에 접근할 수 있는 참조변수로 사용함

메서드의 상속

부모 클래스가 가지고 있는 메서드가 자식 클래스로 상속되어 자식 클래스에서 사용 가능함

메서드의 Overriding

부모 클래스의 메서드를 재사용하지 않고 새롭게 정의하여 사용하는 것 -> 메서드 Overriding

메서드 Overriding은 메서드 재정의라고 함 VS. 메서드 Overloading은 하나의 클래스에서 동일한 이름의 메서드 여러 개를 정의함

상속 관계에 있는 클래스에서 자식 클래스가 부모 클래스의 메서드를 재정의해서 가지고 있는 개념

자식 클래스에서 재정의됨 메서드는 부모 클래스의 메서드와 메서드 이름, 매개변수의 유형과 개수가 동일해야 함

class SuperClass3{
	void print(String str) {
		System.out.println("Super의 print");
	}
}

class SubClass extends SuperClass3 {
	void print() {
		System.out.println("Sub의 print");
	}
}

public class OverridingTest3 {
	public static void main(String[] args) {
		SubClass sub = new SubClass();
		sub.print("테스트");
		sub.print();
	}
}

SubClass 클래스에는 print(String str)과 print() 두 개의 메서드가 Overloading되어 있음

메서드 Overloading과 Overriding

  • 메서드 Overloading 하나의 클래스에 동일한 이름의 메서드가 여러 개 중복 정의되어 있는 것 메서드 매개변수의 개수나 타입이 달라야 함

  • 메서드 Overriding 상속 관계에 있는 두 개의 클래스에서 부모가 가진 메서드와 동일한 시그니처(리턴 타입, 메서드명, 매개변수)를 가진 메서드가 자식 클래스에서 재정의되어 있는 것

Overriding과 super 예약어

상속된 메서드를 사용하는 방법

  1. 부모 클래스의 메서드를 상속을 통해서 그대로 재사용하는 방법
  2. Overriding을 통해서 부모 클래스의 메서드를 자식 클래스에서 재정의해서 사용하는 방법
class Camera2 {
	String name;
	int sheets;
	
	public void takePicture() {
		System.out.println(name +"으로 "+sheets+"번 사진을 찍는다.");
	}
}

class PolaroidCamera2 extends Camera2 {
	int batteryGage;
	
	public void takePicture() {
		System.out.println(name +"으로 "+sheets+"번 사진을 찍는다.");
		System.out.println(sheets + "장의 사진을 프린트한다.");
		System.out.println(batteryGage);
	}
}

public class OverridingTest2 {
	public static void main(String[] args) {
		PolaroidCamera2 camera = new PolaroidCamera2();
		camera.name = "x50";
		camera.sheets = 3;
		camera.batteryGage = 57;
		camera.takePicture();
	}
}

Overriding은 부모의 메서드를 자식이 새롭게 정의하겠다는 의미이므로 부모가 가진 메서드는 상속되지 않음

메서드를 Overriding 하면서 부모 클래스의 메서드를 사용하고 싶다면?

class Camera2 {
	String name;
	int sheets;
	
	public void takePicture() {
		System.out.println(name +"으로 "+sheets+"번 사진을 찍는다.");
	}
}

class PolaroidCamera2 extends Camera2 {
	int batteryGage;
	
	public void takePicture() {
		//System.out.println(name +"으로 "+sheets+"번 사진을 찍는다.");
		super.takePicture();
		System.out.println(sheets + "장의 사진을 프린트한다.");
		System.out.println(batteryGage);
	}
}

public class OverridingTest2 {
	public static void main(String[] args) {
		PolaroidCamera2 camera = new PolaroidCamera2();
		camera.name = "x50";
		camera.sheets = 3;
		camera.batteryGage = 57;
		camera.takePicture();
	}
}

Overriding과 final 예약어

final 예약어는 변수, 메서드, 클래스 앞에 붙일 수 있는 Modifier

  • final + 변수 = 상수를 의미
  • final + class = 자식 클래스를 가질 수 없는 클래스임을 의미
  • final + 메서드 = Overriding을 금지하는 의미
class Printer {
	String name;
	int sheets;
	
	public void ready() {
		System.out.println(name + "프린터를 예열한다.");
	}
	public void printLogic() {
		ready();
		for(int i = 0; i < 3; i++) {
			System.out.println(name+"로"+sheets+"장 씩 출력한다.");
		}
		close();
	}
	public void close() {
		System.out.println(name+" 프린터를 종료한다.");
	}
}

class DotPrinter extends Printer {
	int batteryGage;
	
	public void printLogic() {
		// ready(), close() 메서드 호출이 없기 때문에 문제가 발생
		for(int i = 0; i < 2; i++) {
			System.out.println(name+"로"+sheets+"장 씩 출력한다.");
		}
		System.out.println("현재 배터리"+batteryGage);
	}
}