「Hello world」を読み解く

## 概要

春なので、初心にかえって 「Hello world」を書いてみました。
皆さんご存じのとおり、「Hello world」は入門用のシンプルなコードです。
しかしその反面、その言語に入門したばかりの初心者には分かりづらい、特徴的な箇所も若干あります。

この投稿では、「Hello world」の特徴的な箇所を解説してみます。
たかが「Hello world」、されど「Hello world」。

## HelloWorld.java

まずは、この投稿の主役となるコードを見てください。
ごくごく普通の「Hello world」です。

```java:HelloWorld.java
public class HelloWorld {

  public static void main(String[] args) {
    System.out.println("hello, world");
  }

}
```

```txt:HelloWorld.classの実行結果
hello, world
```

## 「Hello world」の特徴的な箇所

それでは、(一般的なJavaプログラムと比べて)「Hello world」の特徴的な記述を見てみましょう。
各項目には**私の独断で**「特殊度」をつけました。★が多いほど特殊(一般的なプログラムでは見かけない記述)ということになります。


### `package`宣言がない (特殊度: ★★★)

一般的なJavaプログラムは`package`(パッケージ)宣言から始まります。
しかし、`HelloWorld.java`には、`package`宣言がありません。

```java:package宣言の例
package com.example.xyz;
```

`package`宣言がないと、クラスは「デフォルトパッケージ」という特殊なパッケージに属することになります。
「デフォルトパッケージ」は「無名パッケージ」と呼ばれることもあります。

「デフォルトパッケージ」に属するクラスは、他のクラスから`import`(インポート)することができません。
`import`宣言では、「パッケージ名を含めたクラス名[^fqcn]」を指定する必要があるからです。

```java:import宣言の例
import com.example.xyz.HelloWorld;
```

#### 特殊度は?
他のクラスから`import`できないクラスは不便なので、「デフォルトパッケージ」はサンプルコードのような使い捨てのコードでしか使いません。

使い捨てでない一般的なプログラムで`package`宣言が存在しないことはまずないので、特殊度は**★★★(3)**とします。


### クラス宣言に、`extends`がない (特殊度: ☆☆☆)

Javaでは、すべてのクラスは`Object`クラスのサブクラスとなります。
`Object`クラスの直接のサブクラスの場合、`extends Object`の記述は省略することができます。
つまり、`HelloWorld`クラスは、`Object`クラスの直接のサブクラスとなります。

```java:Objectクラスの直接のサブクラスを宣言する例
// 以下のクラス宣言と全く同じ意味
public class HelloWorld {
  // ...
}

// Objectクラスの直接のサブクラスであることを明示
public class HelloWorld extends Object {
  // ...
}
```

#### 特殊度は?
`extends Object`をわざわざ明記することは通常ありません[^extends]。
というわけで、特殊度は**☆☆☆(0)**です。


### `main`メソッドが定義されている (特殊度: ★☆☆)

`main`メソッドは、特殊なメソッドです。
`java`コマンドでは、実行時に指定されたクラスの`main`メソッドが呼び出される仕様となっています。

以下、[`java`コマンドの公式ドキュメント][link-tools_java]から引用します。

> java コマンドは Java アプリケーションを起動します。これは、Java Runtime Environment を起動し、指定されたクラスをロードし、そのクラスの main メソッドを呼び出すことによって行われます。
>
> このメソッドは、public および static として宣言する必要があります。また、値を返してはいけません。さらに、String 配列をパラメータとして指定できなければなりません。メソッド宣言の形式は次のとおりです。
> 
> `public static void main(String[] args)`

上記のとおり、以下が`java`コマンドから呼び出されるメソッドの条件となります。

- メソッド名が、`main`である
- アクセス修飾子が、`public`である
- `static`(静的メソッド)である
- 戻り値が、`void`である
- 引数が、`String[]`(String配列)である

なお、指定されたクラスに呼び出し可能な`main`メソッドが存在しない場合、`java`コマンドは以下のようなエラーになります。

```txt:mainメソッドが存在しない場合のエラー例
> java HelloWorld
エラー: メイン・メソッドがクラスHelloWorldで見つかりません。次のようにメイン・メソッドを定義してください。
   public static void main(String[] args)
```

親切ですね。

#### 特殊度は?
特に特殊な記述というわけではありませんが、規模の大きなプログラムの場合、殆どのクラスは`main`メソッドを持ちません。
もしかしたら`main`メソッドを使ったことがないJavaプログラマーもいるかもしれない[^main]ので、特殊度は**★☆☆(1)**としておきます。

## 「Hello world」の具体的な処理

長くなったので、別投稿にしました。
→ [System.out.println("hello, world")を読み解く][link-sysout]

## HelloWorld.java(冗長な記述)

ここまでの解説をコードに反映してみました。
コメントも沢山いれたので、読んでみてください。

```java:HelloWorld.java(冗長な記述)
// pacakege宣言を省略することで、このクラスはデフォルトパッケージに所属する

import java.lang.*;
// ※以下、java.langパッケージのクラスはパッケージ名の修飾なしで使用可能だが、例示のためにFQCNで記述する

// このクラスはObjectクラスの直接のサブクラスとなる
public class HelloWorld extends java.lang.Object {

  // javaコマンドから呼び出される条件を備えたメソッドを定義する
  public // ・アクセス修飾子が、publicである
  static // ・static(静的メソッド)である
    void // ・戻り値が、voidである
    main // ・メソッド名が、mainである
  (java.lang.String[] args) // ・引数が、String[](String配列)である
  {
    // Systemクラスのoutフィールドから、『「標準」出力ストリーム』を取得する
    java.io.PrintStream ps = java.lang.System.out;

    // Stringインスタンスを、文字列リテラルを使用せずに生成する
    char data[] = { 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd' };
    java.lang.String str = new java.lang.String(data);

    // printlnメソッドで、strをストリームに出力する
    ps.println(str);
  }

}
```

## さいごに

さいごにもう一度、普通の「Hello world」を見てみましょう。

```java:HelloWorld.java
public class HelloWorld {

  public static void main(String[] args) {
    System.out.println("hello, world");
  }

}
```

こうしてみると、「Hello world」から色々と学べることがあるということが分かりました。
たかが「Hello world」、されど「Hello world」。


## 参考

- [Java Language Specification - 7.4.2. Unnamed Packages
](https://docs.oracle.com/javase/specs/jls/se7/html/jls-7.html#jls-7.4.2)
- [Java Language Specification - 7.3. Compilation Units](https://docs.oracle.com/javase/specs/jls/se7/html/jls-7.html#jls-7.3)

リンク先がJava7なのは、なんとなく多くの人が使ってそうな気がしたからです。
この投稿の内容自体は、基本的にはどのバージョンでも同様に通用します。

[link-sysout]:https://qiita.com/pale2f/items/5d19adc954c3572ff5cb
[link-tools_java]:https://docs.oracle.com/javase/jp/7/technotes/tools/windows/java.html
[link-java_lang]:https://docs.oracle.com/javase/jp/7/api/index.html?java/lang/package-summary.html
[link-system]:https://docs.oracle.com/javase/jp/7/api/index.html?java/lang/System.html
[link-printstream]:https://docs.oracle.com/javase/jp/7/api/index.html?java/io/PrintStream.html

[^fqcn]: 完全修飾クラス名。「Fully Qualified Class Name」を略して「FQCN」といわれます。
[^extends]: 私自身、この記事のような「説明用のコード」でしかみたことがないです。
[^main]: サーバサイドプログラムやフレームワークを使用している場合など、`main`メソッドを記述しなくても動作するプログラムは多々あります。