주석에 관한 이야기를 잠깐 해보자!
"왜(Why)를 설명하는 주석을 달아라. 무엇(What)은 코드가 보여줄 것이다."
"무엇(WHAT)를 설명하는 주석을 달아라. 어떻게(How)는 코드가 보여줄 것이다."
조금 당황스럽지만 어쨌든 How를 적지 않는 것은 확실해보인다.
그러나 상속용 클래스는 "어떻게" 까지 적어야 한다!
이후 있을지 모르는 오버라이딩으로 인한 오동작을 방지하기 위해서..!!
특히 어떤 메서드가 재정의 될 가능성이 있으면서, 해당 다른 메서드에서도 호출되는 코드라면, 상속관계에서의 재정의가 의도하지 않은 방식으로 동작할 수 있기 때문이다..
재정의 메서드란
pubilc
,protected
메서드 중final
이 아닌 모든 메서드를 말하며 결국 재정의 될 수도 있는 메서드를 뜻한다.
자기 사용 코드란
클래스 내부의 다른 메서드에서 호출되는 메서드를 말한다.
@implSpec
사용 활성화 방법
1. 명령줄에서 직접 사용:
```
bashCopyjavadoc -tag "implSpec:a:Implementation Requirements:" -d docs src/*.java
```
Maven에서 설정:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<tags>
<tag>
<name>implSpec</name>
<placement>a</placement>
<head>Implementation Requirements:</head>
</tag>
</tags>
</configuration>
</plugin>
Gradle에서 설정:
javadoc {
options.tags = [ 'implSpec:a:Implementation Requirements:' ]
}
IDE에서 설정 (예: IntelliJ IDEA):
-tag "implSpec:a:Implementation Requirements:"
Hook
메서드 제공하기만약 하위 메서드에서 상위 클래스의 내부 동작 중간에 어떤 동작을 끼워넣고 싶다면 어떻게 하는게 좋을까?
Hook
메서드를 쓴다면 안전하게 메서드의 기능을 확장할 수 있다.
원하는 상위 클래스 메서드 중간에 동작을 하지 않는 Hook
메서드를 호출하는 것이다.
이때 상위 클래스는 오버라이딩할 수 없게 final
키워드를 붙여야한다.
public abstract class Pizza {
public final void prepare() {
System.out.println("치즈 추가");
addToppings(); // Hook
System.out.println("굽기");
}
protected void addToppings() {
}
}
//하위 클래스
class PepperoniPizza extends Pizza {
@Override
protected void addToppings() {
super.addToppings(); // 치즈는 그대로 쓰고
System.out.println("페퍼로니 추가");
}
}
하위 클래스의 인스턴스가 생성되기 위해서는 상위클래스의 생성자를 먼저 호출한다는 사실은 모두 아실거라 생각합니다.
만약 상위 클래스 생성자 내부에 하위 클래스에서 오버라이딩 된 메서드를 호출한다면, 오버라이딩되기전 동작이 실행되기 때문이다.
비슷하게 clone
, readObject
메서드 모두 재정의 가능한 메서드를 호출해서는 안된다.
그렇다면 생성자에서 호출해도 안전한 메서드는 무엇일꺼요?
private
,static
,final
메서드 처럼 재정의가 불가능한 메서드들이다.
저자는! 절대적인 규칙이나 판단근거는 없기 때문에, 직접 하위 클래스를 만들어보는 것이 가장 정확한 방법이라고 말한다.
또한 검증을 위해서는 3개의 하위클래스와 하나 이상은 제 3자가 작성해야한다고 말했다.
흠... 이 부분에 대한 다른 분들의 생각이 궁금합니다!
예전에 한 어플 대표님께서 "초반에는 2주에 하나씩 어플을 만들었다. 스파게티 코드라고 하더라도, " +
"출시가 중요했고, 스파게티 코드가 필요한 상황이었다."라고 말씀하셨던 기억이 나네요!
위의 복잡한 문제, 특히 자기 사용 코드와 재정의 가능한 메서드가 주는 잠재적인 어려움을 방지하는 좋은 방법은 무엇일까?
바로 상속을 금지하는 것이다.
상속을 금지하는 방법
- 클래스를
final
로 선언한다.- 생성자를
private
,package-private
로 선언하고 정적 팩터리 메서드를 둔다.
조금 더 절충적인 대안은 클래스 내부에서는 재정의 가능 메서드를 호출하지 않고, 그런 코드는 삭제하는 것이다.
이럴 경우 하위 클래스에서 해당 메서드를 오버라이딩했더라도, 다른 동작과의 관련이 없어 영향이 적기 때문이다.
해당 글은 카카오테크스터디 중에 작성한 글입니다 🙇♀️
제가 정리하지 않은 나머지 글은 고마운 팀원분들이 정리해주시고 계십니다 🫶더 많은 글을 읽고 싶다면 아래 깃허브를 참고해주세요!