読者です 読者をやめる 読者になる 読者になる

せかいや

いまいるここを、おもしろく http://sekai-in-the-box.appspot.com/

【Java】「String.class」はリテラル記法。クラスメソッドのレシーバーって? パーフェクトJava学習感想文 その7

パーフェクトJavaを読んで改めてJavaを振り返り中。


 
え?我が家の育児ですか?
基本セルフサービスかな。

この母親自体も言語獲得に必死なので、
子供というよりもはや同志の感があります。。


 
経緯については学習記録その1をご参照ください。

以下、学んだこと。

「String.class」はリテラル記法

なんと!

調べてみるも、日本語の仕様には記載がない。
でもJLS(Java SE 7 Edition)には記載があった↓

A class literal is an expression consisting of the name of a class, interface, array, or primitive type, or the pseudo-type void, followed by a '.' and the token class.

http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.8.2

新しい記法なのかな?
とりあえず、リテラルであることが分かった。


 
「String.class」って、
今までは、Stringというオブジェクト参照を左オペランド
オペランドをメソッド名(class)としたアクセス演算を実行していると思っていた。

でも考えたら、Stringという識別子は宣言されていないのだから、そもそも
「Stringというオブジェクト参照」なんてない。
だったら、
『クラス変数・クラスメソッド名』は、一体何者なんだ?
 

public class Test {	
	static void hoge(){}
	public static void main(String[] args) {
		Test.hoge();
		
		//エラー。Testっていう変数はないから当然
		System.out.println(Test);
	}
}

この、「Test.hoge()」ってやつ。


 

そもそも「.」は演算子ではない。

本には、

newは演算子
.(ドット)もアクセス演算子

と書いてあったけど、仕様を確認したら演算子ではなかった。
初心者用(?)だから、簡便のために演算子みたいなもの、という意味で演算子と書いたのかな?

 

Java仕様での演算子

= > < ! ~ ? :

== <= >= != && || ++ --

+ - * / & | ^ % << >> >>>

+= -= *= /= &= |= ^= %= <<= >>= >>>=

http://www.y-adagio.com/public/standards/tr_javalang/3.doc.htm#230663

 
と思ったら、JLS(Java SE 7 Edition)ではドットも演算子として定義されてる。

Separator: one of
( ) { } [ ] ; , .

http://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.12


びっくりー。
誤記かな?

 
あくまでnewはキーワード。

Javaコンストラクタ は,newキーワードによって呼び出される。

http://www.y-adagio.com/public/standards/tr_javalang/javacls0.htm

 
でも、「ドット演算子」という記述も見られる。。

・・・メソッド呼び出し式のドット演算子(.),メソッド名,及び左側かっこの左側にあるオブジェクトを値として取る部分式があり・・・

http://www.y-adagio.com/public/standards/tr_javalang/16.doc.htm

 
でも、メソッド呼出し式、の項目において「ドット演算子」の記述はない。

 
ううむ。
発端だった「Hoge.method()」について改めて調べてみる。
「Hogeという識別子がないのになぜドット演算子がエラーとならないのか」
については、そもそもドットは演算子ではないから、という理解ができた。
ちょっと危ういけど。


 
再度、Javaの仕様を確認してみる。

・・・クラスPoint のクラス変数 origin を,Point.origin のように限定名としてクラス名を使用する方法・・・

http://www.y-adagio.com/public/standards/tr_javalang/8.doc.htm#37544

なんと!「限定名」という新たな概念が出てきた。

 

限定名とは

 

限定名は,パッケージ又は参照型のメンバへのアクセスの方法とする。

http://www.y-adagio.com/public/standards/tr_javalang/6.doc.htm#33916

 

  • 限定名
  • フィールドアクセス式
  • メソッド呼出し式

・・・これらはまとめて、限定アクセス(qualified access) のための構文要素として知られる。

ふむ。。


 
あったぞ!今調べているのはこの形での呼び出しだ!

MethodInvocation:
Primary . Identifier ( ArgumentListopt

http://www.y-adagio.com/public/standards/tr_javalang/15.doc.htm#20448

 

もし形式がPrimary . Identifierというものなら,メソッドの名前はそのIdentifierで, 探すべきクラス又はインタフェースはそのPrimaryの型である。

なるほど!
Hoge.method();と書いたら、
Hogeの型を探す、という仕様になっているわけか。

最新の仕様を見ても、この記述は変わらない。
やっぱりドット演算子による演算が実行されているわけではなさそうだ。

If the form is Primary . NonWildTypeArgumentsopt Identifier, then the name of the method is the Identifier.
Let T be the type of the Primary expression. The class or interface to be searched is T

http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.12


Rubyとの違いも見えてきたぞ。
 
RubyではHogeクラスを作ると、暗黙的に定数Hogeに
Hogeクラスに対応するClassクラスオブジェクトへの参照が格納される。
その結果、Hoge.method()は
レシーバ「Hoge」によるmethodメソッドの実行、という意味を持てる。

Javaではそういう動きはないから、
こんな仕様(※1)になっている。

※1
Hoge.method();と書いたら、
Hoge識別子の参照する先を探すのではなく(そもそもない)、
Hogeの型を探す、という仕様
 

ふむふむ。