聞くところJava本格入門(通称アクロ本)の評判がすこぶる良いようだ。1995年にJavaが発表されてから随分と経つ。Javaのメジャーバージョンも8を数え、9がリリースされようとしている。1990年代にはこの新しい言語についてたくさんの技術書が発行されたが、これほどバージョンに差異があると現代では役に立たない。現代のバージョンに即した質の良い入門書が発行されることは喜ばしいことだと思う。
しかしながら、このJava本格入門も3-4-2 ジェネリクス(総称型)にていささか不適切な用語の使用方法がされているようだ。本項では改善提案を行い、また関連する用語の解説を行いたい。
発端
Java本格入門の101p、何となく違和感…
— なめりかん(仮) (@heignamerican) 2017年5月11日
『GenericStack<E>のように、パラメータ化された型として定義』
ここパラメータ化された型ではなく総称型ではなかろうか?
これに端を発しThe Java® Language Specification Java SE 8 Edition(Javaの言語仕様)を引用しての用語検討が行われた。
改善案
Java本格入門の原文は次のような文である。(3-4-2 ジェネリクス(総称型) より。強調は私によるもの)
それではジェネリクスを利用して、任意の型を追加可能なスタックであるGenericStackクラスを作成してみましょう。先ほどのStringStackクラスでは、taskListフィールドの要素の型やpushメソッドの引数、popメソッドの戻り値の型がStringでしたが、任意の型にするために、これらを仮の型であるEという文字で表現することにします(文字はEでなくてもかまいません)。この仮の型であるEを、仮型パラメータと呼びます。
ジェネリクスを定義するには、仮型パラメータEを用いて、GenericStack<E>のように、パラメータ化された型として定義します。
私による修正案は以下の通り。
それではジェネリクスを利用して、任意の型を追加可能なスタックであるGenericStackクラスを作成してみましょう。先ほどのStringStackクラスでは、taskListフィールドの要素の型やpushメソッドの引数、popメソッドの戻り値の型がStringでしたが、任意の型にするために、これらを仮の型であるEという文字で表現することにします(文字はEでなくてもかまいません)。この仮の型であるEを、型変数と呼びます。
型変数を定義するには、型パラメータEを用いて、GenericStack<E>のように、総称型として定義します。
引数、このややこしいもの
ジェネリクスでは型を取り扱うために「型変数」(Type Variables)を取り扱う。型を扱うための変数で型変数、というわけだ。
ここで、メソッドの「引数」の話をしよう。この「引数」という用語、非常に混乱がみられる。
public void foo(int value) { ... }
といったメソッドがあったとして、
foo(123);
といった形で呼び出されたとする。
この時、宣言側のvalueという変数を指して、parameter, formal parameter, formal argument, パラメータ, 仮引数, 仮パラメータ といった呼びかたをする。
また、123という実際に引き渡す値のことを指して、argument, actual parameter, actual argument, アーギュメント, 実引数, 実パラメータ といった呼び方をする。
単に「引数」と呼んだ場合、前者のことを指すこともあれば、後者のことを指すこともある。非常に曖昧で混乱がみられる用語である。
value側 | 123側 |
---|---|
parameter | argument |
formal parameter | actual parameter |
formal argument | actual argument |
パラメータ | アーギュメント |
仮引数 | 実引数 |
仮パラメータ | 実パラメータ |
このあたりはパラメータと引数 - Life like a clownを大いに参考にさせてもらった。日本語圏だけでなく、英語圏でもparameter, argumentの使い分けについては混乱がみられるようだ。
用語の混乱はあるが、本稿では宣言側をパラメータ、渡す値をアーギュメントと呼ぶことにしよう。
変数の受け渡しに注目する文脈で、変数を「パラメータ」「アーギュメント」と呼び分けるように、型変数についても受け渡しする文脈で「型パラメータ」「型アーギュメント」と呼び分ける必要が生じる。
3種の山括弧
Javaのジェネリクスについて、単に「ジェネリクス」という用語でジェネリクスに関するもの全体をもやっと指すという使われ方をしているように思う。区別ができないからもやっと「ジェネリクス」と呼んでいる状態。とくに山括弧<>があればなんとなく雰囲気で「ジェネリクス」と呼んでいる人が多いだろう。
まずはJavaのジェネリクスの山括弧には構文的に3種類あることを理解しなければならない。
- 型変数の宣言での型パラメータ
- 型変数へのバインディングでの型アーギュメント
- パラメータ化された型(パラメタライズドタイプ)での型アーギュメント
の3種類だ。(本稿の性質上用語の厳密さを気にしていたら妙に回りくどい表現になってしまった)
public class Hoge<T> {}
public class Piyo { public static void main(String[] args) { Hoge<String> hoge = new Hoge<String>(); }
というコードがあったあったとき、
- 型変数の宣言時の型パラメータ → Hoge<T> の<T>
- 型変数へのバインディングでの型アーギュメント → new Hoge<String>(); の<String>の部分。
- パラメータ化された型 → Hoge<String> hoge = ... の Hoge<String>
- パラメータ化された型での型アーギュメント → Hoge<String> hoge = ... の<String>
となる。詳細はやや古いが拙稿のJavaジェネリクス再入門 - プログラマーの脳みそあたりも参考にして欲しい。
Hoge<T>では型変数Tが宣言される。メソッドの引数のときの例で言えばfoo(int value)のvalueに相当するのがTと言えよう。これに対し、new Hoge<String>();ではこのTにString型をあてますよ、という意味合いになる。foo(123);の123に相当するものがStringというわけだ。TにStringをあてる部分を私は「バインディング」と表現している(情報工学用語では「束縛」英語では"Binding")。こうした、型変数を宣言する/バインディングするという文脈では「型パラメータ」「型アーギュメント」を厳密に使い分ける必要が生じる。しかし、そうした文脈でなければ型変数は単に「型変数」と呼べば良い。
ここで、Java言語仕様の英文での表現をみると
- 型変数の宣言時の型パラメータ Type Parameters 8.1.2. Generic Classes and Type Parameters
- 型変数へのバインディングでの型アーギュメント Type Arguments 15.9. Class Instance Creation Expressions
- パラメータ化された型 Parameterized Types Type Arguments of Parameterized Types
- パラメータ化された型での型アーギュメント Type Arguments Type Arguments of Parameterized Types
となっている。また単に「型変数」という場合はType Variables 4.4. Type Variablesという語が使われる。こうした型変数をもつクラスを指してはGeneric Classes 8.1.2. Generic Classes and Type Parameters という語が使われる。これはそのままカタカナにしてジェネリック・クラスと表現するか漢字で「総称型」と表現される。Javaの公式の日本語訳書で用いられた漢字表現だ。
Java本格入門の用語のなにが悪いのか
「Java本格入門」は入門書であるから、余計な概念を持ち込んで読者を混乱させることは好ましくないと思う。3-4-2 ジェネリクス(総称型) の章では込み入った構文までは踏み込まないのであるから、型変数を宣言する/バインディングするという概念を持ち込む必要はないのではないか。
「仮型パラメータ」という語は、(型変数ではない通常の)変数でいうところの「仮パラメータ」- 「実パラメータ」という対比で用語選定した上で「型」をつけたものだと思われる。この用語の用例はOracleの公式のドキュメントにも存在することは存在する(ジェネリック・インスタンス作成のための型推論)。しかし、そもそもこのあたりの訳語についてはOracle(や前身のSun)の公式の書籍などでも統一されていない。
先に挙げたドキュメントの英語版では"the formal type parameter", "the actual type parameters"という語を使っており、日本語ドキュメントでは「仮型パラメータ」「実型パラメータ」という訳語をあてている。Java言語仕様での用語とすでに違う。
型変数を宣言する/バインディングするという文脈を厳密に語ろうとすれば、これら混乱した用語の中から用語を、そしてその訳語を探さなくてはならない。これは入門書の領域を脱していると思う。なので単に「型変数」で済ませるほうが良いのではないか。だいたい「pushメソッドの引数」といった風に普通の変数については「引数」とざっくりした宣言/バインディングを意識しない表現をして、型変数についてだけ「仮型パラメータ」と妙に意識した表現をするというのは不自然ではないか。
また、ふわっと「ジェネリクスを定義するには」と表現している部分は明確に「型変数を宣言するには」あるいは、「総称型のクラスを宣言するには」といった表現を用いたほうがよいだろう。
「パラメータ化された型として定義します」の下りについては、Javaのジェネリクス関連用語として「パラメータ化された型」という専門用語が存在しており(私はこの1単語に見えない用語を嫌って「パラメタライズドタイプ」と表現する方がよいと考えている)これは本稿の例ではHoge<String> hoge = ... の Hoge<String>と解説した。型変数を加えることを「パラメータ化」という言い方をする用例もあるのだが、「パラメータ化された型」という専門用語が存在してしまっている以上、紛らわしい表現は避けるべきだろう。