なぜServletのdoXXメソッドはprotected?

新人研修の中で、HttpServlet の doGet() などのメソッドがなぜ protected なのか疑問に思い、調べている方がいました。習ったばかりの Java のアクセス修飾子の知識を整理しながら自分で結論にたどり着いており、とても素晴らしい考察だったので共有します。
なぜServletのdoXXメソッドはprotected?
Servletを学んでいると、必ず出てくるのが doGet()
や doPost()
などの doXX
メソッド。このメソッド群はすべて protected で宣言されています。なぜ public ではなく protected なのか?アクセス修飾子の観点から整理してみます。
Servlet の基本設計
Servlet API では、HttpServlet
という抽象クラスが用意されています。このクラスが提供する doGet()
, doPost()
などのメソッドを開発者がオーバーライドして処理を実装するのが基本スタイルです。
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
// 処理を実装
}
}
つまり、継承してメソッドをオーバーライドして利用することが前提となっています。
アクセス修飾子の選択肢を整理
ここで、もしアクセス修飾子が違ったらどうなるか?を一つずつ見ていきます。
アクセス修飾子なし(デフォルト / package-private)
- 同一パッケージ内のクラスからはアクセス可能
- 別パッケージのクラスからはアクセス不可
Servlet API は jakarta.servlet.http
パッケージに定義されています。通常、開発者が実装するサーブレットクラスは全く別のパッケージに属します。
そのため、もしデフォルト修飾子だった場合、開発者がオーバーライドすることができなくなってしまいます。 → NG
public
- サブクラスからアクセス可能
- 別パッケージの他のクラスからもアクセス可能
public にすると継承したクラスからのオーバーライドはもちろん可能ですが、全く無関係のクラスからも直接呼び出せるようになってしまいます。
例えば、以下のようなコードが書けてしまいます。
HttpServlet servlet = new HttpServlet();
servlet.doGet(request, response); // 外部から直接呼び出し
Servlet APIの本来の設計思想では、doGet()
や doPost()
は コンテナ(TomcatなどのWebサーバ)が内部的に呼び出すべきメソッドであり、開発者や他のクラスが直接呼ぶべきものではありません。
→ これもNG
protected
- サブクラスからアクセス可能(別パッケージでもOK)
- 同一パッケージ内のクラスからもアクセス可能
- 全く無関係の別パッケージ・非サブクラスのクラスからはアクセス不可
protected にすると、継承先の開発者はオーバーライドして実装可能になります。そして、フレームワークの設計通り、外部の無関係なクラスが直接呼び出すことはできなくなります。
まさに Servlet API の意図にぴったり合致します。
→ これが最適解
まとめ表
アクセス修飾子 | オーバーライド可否 | 外部クラスからの呼び出し |
---|---|---|
アクセス修飾子なし | ❌ | ❌ |
public | ✅ | ✅(望ましくない) |
protected | ✅ | ❌(理想的) |
結論
HttpServlet の doXX()
メソッドが protected なのは、まさに以下の理由のため:
- 継承してオーバーライドして使ってほしい
- しかし外部の無関係なクラスから直接呼び出されるのは防ぎたい
フレームワーク型API設計において、protected の使い方は非常に理にかなっています。
補足:protectedは「拡張ポイントを提供するための修飾子」
実はJavaにおける protected という修飾子は、フレームワーク設計で非常によく使われます。利用者にカスタマイズさせたいが、完全には公開したくないときのバランス型の公開範囲として重宝されています。