본문 바로가기

Java 기본 문법 - 참조 서적 [이것이 자바다 - 한빛미디어]/5. 상속, 다형성

2. Java 자바 - 메소드 재정의 @Overrride, final 클래스, final 메소드

부모 클래스의 모든 메소드가 자식 클래스가 사용하기 적합하지 않는 경우가 있다.

 

이때 상속된 일부 메소드는 자식 클래스에서 다시 수정해서 사용한다. (메소드 오버라이딩)

 

메소드 재정의 (@Override)

 

상속된 메소드 내용이 자식 클래스에 맞지 않을 경우

자식 클래스에서 동일한 메소드를 재정의한다.

 

메소드가 오버라이딩되었다면, 부모 객체의 메소드는 숨겨지기 때문에

자식 객체에서 메소드를 호출하면 오버라이딩된 자식 메소드가 호출된다.

 

 

메소드 오버라이딩

 

 

메소드 오버라이딩 시 주의할 점

 

- 부모 메소드와 동일한 시그니처(리턴 타입, 메소드 이름, 매개 변수 리스트)를 가져야 한다.

 

- 접근 제한을 더 강하게 다시 설정하여 오버라이딩 할 수 없다.

  ( public < default < protected < private )

 

- 새로운 예외(Exception)를 throws 할 수 없다. (예외는 나중에!)

 

 

접근 제한을 더 강하게 오버라이딩 할 수 없는 의미

 

  부모 메소드가 public 접근 제한을 가질 때,

  오버라이딩하는 자식 메소드는 default, private 접근 제한으로 수정할 수 없다.

 

  부모 메소드가 default 접근 제한을 가지면,

  재정의 되는 자식 메소드는 default 또는 public 접근 제한을 가질 수 있다.

 

ex) Calculator 의 자식 클래스 Computer에서 원 넓이를 구하는 Calculator 의 areaCircle( ) 메소드를

사용하지 않고 좀 더 정확한 원 넓이 구하도록 오버라이딩

 

Calulator.java : 부모 클래스

public class Calculator {

    //메소드
    double areaCircle(double r) {
        System.out.println("Calculator 객체의 areaCircle() 실행");
        return 3.14159 * r * r;
    }
}

 

Computer.java : 자식 클래스

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

 

오버라이딩된 메소드는 Math.PI 상수를 이용한다.

Math 는 수학 계산과 관련된 필드와 메소드들을 가지고 있는 클래스로, 자바 표준 API를 제공한다.

 

@Override 어노테이션은 생략 가능

하지만, 입력하면 컴파일러가 오버라이딩 처리를 체크하기 때문에 개발자 실수를 줄여준다.

 

 

ComputerExample.java : 메소드 오버라이딩 테스트

public class ComputerExample {
    public static void main(String[] args) {
        int r = 10;
        
        Calculator calculator = new Calculator();  //부모 객체 생성
        System.out.println("원 면적 : "+ calculator.areaCircle(r));
        System.out.println();
        
        Computer computer = new Computer();     // 자식 객체 생성
        System.out.println("원 면적 : "+ computer.areaCircle(r));  // 재정의된 메소드 호출
    }
}

 

 

* 이클립스에서 부모 메소드 중 하나를 선택해서 오버라이딩 메소드를 자동 생성하기

(부모 메소드의 시그니처를 정확히 모르는 경우 유용하다.)

 

    1. 자식 클래스에서 오버라이딩 메소드를 작성할 위치로 입력 커서를 옮긴다.

    2. 메뉴에서 [Source Override/Implement Methods...] 를 선택한다.

    3. 부모 클래스에서 오버라이딩 될 메소드를 선택하고 [OK] 누른다.

 


 

1. 부모 메소드 호출 (super)

 

자식 클래스에서 부모 클래스의 메소드를 오버라이딩하면,

부모 메소드는 숨겨지고, 오버라이딩된 자식 메소드만 사용된다.

 

하지만, 자식 클래스 내부에서 오버라이딩된 부모 클래스의 메소드를 호출해야 하는 경우,

 

명시적으로 super 키워드를 붙여서 부모 메소드를 호출할 수 있다.

 

super 는 부모 객체를 참조하고 있기 때문에 부모 메소드에 직접 접근할 수 있다.

 

super.부모메소드();

 

super로 부모 메소드 호출

 

 

ex) Airplane 클래스(부모)를 상속해서 SupersonicAirplane 클래스 만들기

Airplane 클래스의 fly( ) 메소드는 일반 비행

SupersonicAirplane 클래스의 fly( ) 메소드는 초음속 비행

 

Airplane.java : 부모 클래스

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

 

SupersonicAirplane.java : 자식 클래스

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

 

2, 3 라인에서 상수가 선언되었다. (자주 사용되는 고정값들은 상수 사용으로 가독성을 높인다.)

 

fly( ) 메소드는 오버라이딩 되었다.

 

flyMode 가 SUPERSONIC 상수값을 가질 경우, "초음속비행합니다." 출력,

그렇지 않을 경우, 부모 클래스 Airplane 의 fly( ) 메소드 호출하기 위해 super.fly( ) 사용

 

SupersonicAirplaneExample.java

public class SupersonicAirplaneExample {
    public static void main(String[] args) {
        SupersonicAirplane sa = new SupersonicAirplane();
        
        sa.takeOff();                               // 이륙
        sa.fly();                                   // 일반비행
        sa.flyMode = SupersonicAirplane.SUPERSONIC; 
        safly();                                    // 초음속비행
        sa.flyMode = SupersonicAirplane.NORMAL;
        sa.fly();                                   // 일반비행
        sa.land();                                  // 착륙
    }
}

 

 


 

2. final 클래스와 final 메소드

 

final 키워드는 클래스, 필드, 메소드 선언 시에 사용할 수 있다.

 

final : 해당 선언이 최종 상태이고, 더 이상 수정될 수 없음.

 

필드에 final 키워드가 지정되면, 초기값 설정 후 더 이상 값을 변경할 수 없다.

final 필드와 상수

 

final 키워드가 클래스, 필드, 메소드 선언에 사용될 경우 해석이 조금씩 달라진다.

 

클래스와 메소드에 final 키워드가 적용되면?? (상속과 연관이 생긴다!)

 

 

- 상속할 수 없는 final 클래스

 

클래스 선언 시 final 키워드를 class 앞에 붙이면, 이 클래스는 최종적인 클래스이므로

상속할 수 없는 클래스가 된다.

 

즉, final 클래스는 부모 클래스가 될 수 없어 자식 클래스를 만들 수 없다!!

 

public final class 클래스 { . . . }

 

ex) String 클래스의 선언

 

public final class String { . . }

 

따라서 아래와 같이 자식 클래스를 만들 수 없다.

 

public class NewString extends String { . . } // 에러!!!

 

 

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

 

메소드 선언 시 final 키워드를 앞에 붙이게 되면, 이 메소드는 최종적인 메소드이므로

오버라이딩 할 수 없는 메소드가 된다.

 

즉, 부모 클래스를 상속해서 자식 클래스를 선언할 때,

부모 클래스에 선언된 final 메소드는 자식 클래스에서 재정의할 수 없다.

 

public final 리턴타입 메소드 ( [매개변수], . . ) { . . . }

 

ex) Car 클래스의 stop 메소드를 final 로 선언해서

Car 를 상속한 SportsCar 클래스에서 stop( ) 메소드를 오버라이딩 불가

 

Car.java : 부모 클래스

 

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

 

SportsCar.java : 자식 클래스

 

public class SportsCar extends Car {
    @Override
    public void speedUp() { speed += 10; }   // 오버라이딩 가능
    
    //오버라이딩 불가!! 에러 발생!!
    @Override
    public void stop() {
        System.out.println("스포츠카를 멈춤");
        speed = 0;
    }
}