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 |