본문 바로가기

Java 길찾기/이것이 자바다

[Java] 상속 (2)

final 클래스와 final 메소드

final 키워드는 클래스, 필드, 메소드 선언 시에 사용할 수 있다. final 키워드는 해당 선언이 최종 상태이고, 결코 수정될 수 없음을 뜻한다. final 키워드가 클래스, 필드, 메소드 선언에 사용될 경우 해석이 조금씩 달라지는데, 우리는 이미 앞에서 final 필드에 대해서 살펴보았다. 필드 선언 시에 final이 지정되면 초기값 설정 후, 더 이상 값을 변경할 수 없다는 것을 알았다. 그렇다면 클래스와 메소드에 final이 지정되면 어떤 효과가 날까? 클래스와 메소드 선언 시에 final 키워드가 지정되면 상속과 관련이 있다.

 

상속할 수 없는 final 클래스

클래스를 선언할 때 final 키워드를 class 앞에 붙이게 되면 이 클래스는 최종적인 클래스이므로 상속할 수 없는 클래스가 된다. 즉 final 클래스는 부모 클래스가 될 수 없어 자식 클래스를 만들 수 없다는 것 이다.

public final class 클래스 { ... }

final 클래스의 대표적인 예는 자바 표준 API에서 제공하는 String 클래스이다. String 클래스는 다음과 같이 선언되어 있다.

public final class String { ... }

그래서 다음과 같이 자식 클래스를 만들 수 없다.

public class NewString extends String { ... } // 불가능

 

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

메소드를 선언할 때 final 키워드를 붙이게 되면 이 메소드는 최종적인 메소드이므로 오버라이딩(Overiding)할 수 없는 메소드가 된다. 즉 부모 클래스를 상속해서 자식 클래스를 선언할 때 부모 클래스에 선언된 final 메소드는 자식 클래스에서 재정의할 수 없다는 것이다.

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

 

다음 예제는 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;
    }
}

 

protected 접근 제한자

접근 제한자는 public, protected, default, private와 같이 네 가지 종류가 있다. 이 중에서 protected는 상속과 관련이 있기 때문에 설명을 미루어 왔는데, 이제 protected가 어떤 역할을 하는지 알아보자.

 

접근 제한 적용할 내용 접근할 수 없는 클래스
public 클래스, 필드, 생성자, 메소드 없음
protected 필드, 생성자, 메소드 자식 클래스가 아닌 다른 패키지에 소속된 클래스
private 클래스, 필드, 생성자, 메소드 다른 패키지에 소속된 클래스
default 필드, 생성자, 메소드 모든 외부 클래스

protected는 public과 default 접근 제한의 중간쯤에 해당한다. 같은 패키지에서는 default와 같이 접근 제한이 없지만 다른 패키지에서는 자식 클래스만 접근을 허용한다.

 

protected는 필드와 생성자, 메소드 선언에 사용될 수 있다 .다음 A 클래스를 보면 protected로 선언될 필드, 생성자, 메소드가 있다.

// A.java
package package1;

public class A {
    protected String field;
    
    protected A() {
    }
    
    protected void method() {
    }
}

 

다음 B 클래스는 A 클래스와 동일한 패키지에 있다. default 접근 제한과 마찬가지로 B 클래스의 생성자와 메소드에서는 A 클래스의 protected 필드, 생성자, 메소드에 얼마든지 접근이 가능하다.

// B.java
package package1;

public class B {
    public void method() {
        A.a = new A();      // (o)
        a.field = "value";  // (o)
        a.method();         // (o)
    }
}

 

다음 C 클래스는 A 클래스와 다른 패키지에 있다. default 접근 제한과 마찬가지로 C 클래스의 생성자와 메소드에서는 A 클래스의 protected 필드, 생성자, 메소드에 접근할 수 없다.

package package2;

import package1.A;

public class C {
    public void method() {
        A a = new A();      // (x)
        a.field = "value";  // (x)
        a.method();         // (x)
    }
}

 

다음 D 클래스는 A 클래스와 다른 패키지에 있다. C 클래스와는 달리 D는 A의 자식 클래스이다. 그렇기 때문에 A 클래스의 protected 필드, 생성자, 메소드에 접근이 가능하다. 단 new 연산자를 사용해서 생성자를 직접 호출할 수는 없고, 자식 생성자에서 super()로 A 생성자를 호출할 수 있다.

package package2;

import package1.A;

public class D extends A {
    public D {
        super();               // (o)
        this.field = "value";  // (o)
        this.method();         // (o)
    }
}

'Java 길찾기 > 이것이 자바다' 카테고리의 다른 글

[Java] 타입 변환과 다형성 (2)  (0) 2022.01.24
[Java] 타입 변환과 다형성 (1)  (0) 2022.01.21
[Java] 상속 (1)  (0) 2022.01.19
[Java] 어노테이션  (0) 2022.01.18
[Java] Getter와 Setter 메소드  (0) 2022.01.17