[Java 17] 자바 - 상속(2)

학습목표

  1. 메소드의 재정의 활용법을 정확히 이해했다.
  2. final과 protected가 선언된 코드의 상속 여부에 대해 정확히 이해했다.

메소드의 재정의

상속을 사용하다 보면 부모 클래스의 메소드가 자식 클래스에서 사용하기에 적합하지 않는 경우가 많이 생기게 됩니다. 이 경우 상속된 일부 메소드를 자식 클래스에서 다시 수정해서 사용해야 하는데 이런 경우 메소드 오버라이딩(Overriding) 기능을 통해 가능합니다.

오버라이딩 (@Override)

메소드 오버라이딩은 상속된 메소드의 내용이 자식 클래스에 맞지 않을 경우, 자식 클래스에서 동일한 메소드를 재정의하는 것을 말합니다. 메소드 오버라이딩은 다음과 같은 규칙에 따라 작성해야 합니다.

  • 부모의 메소드와 동일한 시그니처(리턴 타입, 메소드 이름, 매개변수 리스트)를 가져야 한다.
  • 접근 제한자를 더 강하게 오버라이딩 할 수 없다.
  • 새로운 예외(Exception)를 throw할 수 없다.


예제 1

[Calculator] 부모 클래스

public class Calculator {
	double areaCircle(double r) {
		System.out.println("Calculator 객체의 areaCircle() 실행");
		return 3.141592 * r * r;
	}
}

[Computer] 자식 클래스

public class Computer extends Calculator{
	@Override
	double areaCircle(double r) {
		System.out.println("Calculator 객체의 areaCircle() 실행");
		return Math.PI * r * r;
	}
}

[ComputerMain] 메소드 오버라이딩 테스트

public class ComputerMain {
	public static void main(String[] args) {
		int r = 30;

    //부모 클래스 메소드
		Calculator cal = new Calculator();
		double cal1 = cal.areaCircle(r);
		System.out.println("원면적 : " + cal1);
		
    //자식 클래스 메소드
		Computer com = new Computer();
		double com1 = com.areaCircle(r);
		System.out.println("원면적 : " + com1);
	}
}

실행결과

Calculator 객체의 areaCircle() 실행
원면적 : 2827.4328
Calculator 객체의 areaCircle() 실행
원면적 : 2827.4333882308138

메소드 오버라이딩 자동 생성 기능

  1. 자식 클래스에서 오버라이딩 메소드 작성할 위치에 커서를 옮김
  2. [Source > Override/Implement Method…] 를 선택
  3. 부모 클래스에서 오버라이딩될 메소드를 선택하고 OK 버튼을 클릭


부모 메소드 호출(super)

자식 클래스에서 부모 클래스의 메소드를 오버라이딩하게 되면, 부모 클래스의 메소드는 숨겨지고 오버라이딩된 자식 메소드만 사용됩니다. 그러나 코드를 짜다보면 부모 클래스의 메소드를 다시 호출해야할 경우가 생기는데 이때 명시적으로 super 키워드를 붙여서 부모 메소드를 호출할 수 있습니다.

super.부모메소드();


예제 1

[Airplane] 부모 클래스

public class Airplane {
	public void land() {
		System.out.println("착륙합니다.");
	}
	public void fly() {
		System.out.println("일반비행합니다.");
	}
	public void takeOff() {
		System.out.println("이륙합니다.");
	}
}

[SupersonicAirplane] 자식 클래스

public class SupersonicAirplane extends Airplane{
	public static final int NORMAL = 1;
	public static final int SUPERSONIC = 2;
	
	public int flyMode = NORMAL;
	
	@Override
	public void fly() {
		if(flyMode == SUPERSONIC) {
			System.out.println("초음속비행합니다.");
		} else {
			super.fly();	// 부모 메소드 호출
		}
	}
}

[SupersonicAirplaneMain]

public class SupersonicAirplaneMain {
	public static void main(String[] args) {
		SupersonicAirplane sa = new SupersonicAirplane();
		
		sa.takeOff();
		sa.fly();
		sa.flyMode = SupersonicAirplane.SUPERSONIC;
		sa.fly();
		sa.flyMode = SupersonicAirplane.NORMAL;
		sa.land();
	}
}

실행결과

착륙합니다.
일반비행합니다.
초음속비행합니다.
이륙합니다.


final 클래스와 final 메소드

final 키워드는 클래스, 필드, 메소드 선언 시에 사용이 가능합니다. 이미 “[Java 13] final 필드와 상수” 에서 final 필드에 대해 알아봤습니다. 그렇다면 클래스와 메소드에 final이 선언되면 상속과 관련이 있습니다.

1. 상속할 수 없는 final 클래스

클래스 선언시 final 키워드를 붙이면 이 클래스는 최종적인 클래스이므로 상속할 수 없는 클래스가 됩니다. 즉 final 클래스는 부모 클래스가 될 수 없으므로 자식 클래스 또한 만들 수 없는 것입니다.

public final class 클래스 {...}

final 클래스의 대표적인 예가 바로 자바 표준 API에서 제공하는 String 클래스입니다.

public final class String {...}  // 상속이 불가능
public final class NewString extends String {...}  //=> 다음과 같이 만들 수 없다.

위의 코드처럼 NewString 클래스가 String 클래스를 상속할 수 없습니다.

2. 오버라이딩할 수 없는 final 메소드

메소드 선언시 final을 선언하게 되면 이 메소드는 최종 메소드가 되므로 오버라이딩이 불가능합니다. 아래의 코드처럼 SportsCar 클래스는 Car 클래스를 상속받았지만 final 메소드인 stop() 메소드는 자식 클래스에서 재정의할 수 없음을 보여줍니다.

[Car] 클래스

public class Car {
	//필드
	public int speed;
	//메소드
	public void speedUp() {
		speed += 1;
	}
	//final 메소드
	public final void stop() {
		System.out.println("차를 멈춤");
		speed = 0;
	}
}

[SpoertsCar] 클래스

public class SportsCar extends Car {
	public static void main(String[] args) {
		@Override
		public void stop() {
			System.out.println("스포츠카를 멈춤");
			speed = 0;
		}
	}
}

protected 접근 제한자

이미 “[Java 14] 자바 - 접근 제한자”에서 접근제한자에 대해 알아보았다. protected는 상속과 관련이 있어 해당 장에서 알아보도록 하겠습니다. protected는 public과 default의 중간에 해당되는 접근제한자입니다. 동일 패키지 내에서는 default와 같이 접근이 가능하지만 다른 패키지에서는 자식 클래스만 접근이 가능합니다.

예제

package day0501.package1;
public class A {
	protected String field;

	protected A() {
	}

	protected void method() {
	}
}
package day0501.package1;
public class B {
	public void method() {
		A a = new A();
		a.field = "value";
		a.method();
	}
}

위의 코드 클래스 A와 B는 동일한 패키지에 있습니다. 따라서 B클래스의 생성자와 메소드는 A클래스의 필드, 생성자, 메소드에 얼마든지 접근이 가능합니다.

package day0501.package2;
import day0501.package1.A;
public class C {
	public void method() {
		A a = new A();
		a.filed = "value";
		a.method();
	}
}

위의 C 클래스는 A 클래스와 다른 패키지에 있기 때문에 C 클래스의 생성자와 메소드에서는 A클래스의 필드, 생성자, 메소드에 접근이 불가능합니다.

package day0501.package2;
import day0501.package1.A;
public class D extends A {
	public D() {
		super();
		this.field = "value";
		this.method();
	}
}

위의 D 클래스는 A 클래스와는 다른 패키지에 있지만 A클래스를 상속하는 자식클래스 입니다. 따라서 A 클래스의 필드, 생성자, 메소드에 모두 접근이 가능합니다.

태그:

카테고리:

업데이트:

댓글남기기