『徹底攻略 Java SE 11 Gold 問題集』の補足

[徹底攻略Java SE 11 Gold問題集［1Z0-816］対応](https://book.impress.co.jp/books/1121101020)で勉強した際に調べた結果を共有いたします。


## 1章 問題 1

解説で「static インナー クラス」という用語が使われていますが、これは厳密には不正確な表現です。Java 言語仕様には以下のように記述されています。

> An inner class is a nested class that is not explicitly or implicitly declared static.

https://docs.oracle.com/javase/specs/jls/se11/html/jls-8.html#jls-8.1.3

つまり、static なネステッド クラスのことを「インナー クラス」とは呼ばないのです。

本件は『プロになる Java』と説明が違っていたために気がつきました。『プロになる Java』「14.1.6 ネステッドクラスとインナークラス」では以下のように説明されていました。

> クラスの中で定義するクラスをネステッドクラスといいます。

> static が付いていないクラスはインスタンスクラスとは呼ばずにインナークラスといいます。static クラスは正式な用語ではありませんが、static なネステッドクラスを表す場合に使われます。

言語仕様と照らし合わせると、『プロになる Java』の説明のほうが正確な記述だと言えます。


## 1 章 問題 5

Java 15 までは、static でないネステッド クラスに static フィールドは持てず、コンパイル エラーになります。

```java
class Outer {
    class Inner {
        static int field;
    }
}
```

詳しいことは知らないのですが、レコード クラスで問題があったため Java 16 で許容されるようになっていました。

https://qiita.com/nowokay/items/215769cdcb14d6c5412f#inner-class%E3%81%A7static%E3%83%A1%E3%83%B3%E3%83%90%E3%83%BC%E3%81%AE%E5%AE%9A%E7%BE%A9


## 1 章 問題 11

Java 言語仕様には以下の記述があります。

> - If the form is TypeName . super . [TypeArguments] Identifier, then:
>   (中略)
>   - Otherwise, TypeName denotes the interface to be searched, I.
>
>     **Let T be the type declaration immediately enclosing the method invocation. It is a compile-time error if I is not a direct superinterface of T, or if there exists some other direct superclass or direct superinterface of T, J, such that J is a subtype of I.**

https://docs.oracle.com/javase/specs/jls/se11/html/jls-15.html#jls-15.12.1

問題集の interface A は直接のスーパーインターフェースになっていて、他の直接のスーパーインターフェースは A のサブタイプではないので、この形式でデフォルト メソッドを呼び出すことができます。

コンパイル エラーになるのは次の2つの場合です。

### I が T の直接的なスーパーインターフェースでない

以下のような場合です。[JEP 330: Launch Single-File Source-Code Programs](https://openjdk.org/jeps/330) の java コマンドをインタープリターのように使う方法で試しやすいように、main クラスを一番上に書いています。

```java
public class T implements J {
    public static void main(String[] args) {
        new T().methodAtT();
    }
    void methodAtT() {
        I.super.method();
    }
}

interface J extends I {}

interface I {
    default void method() {
        System.out.println("I");
    }
}
```

### I 以外に T の直接的なスーパークラスか直接的なスーパーインターフェース J があり、それが I のサブタイプである。

これは以下のような場合です。

```java
public class T implements I, J {
    public static void main(String[] args) {
        new T().methodAtT();
    }
    void methodAtT() {
        I.super.method();
    }
}

interface J extends I {}

interface I {
    default void method() {
        System.out.println("I");
    }
}
```


## 13 章 問題 8

この挙動が仕様として納得できなかったので検索してみたところ、StackOverflow に同様の質問がありました。

https://stackoverflow.com/questions/49610855/calling-default-method-in-interface-when-having-conflict-with-private-method

これはバグで、現在も open になっています。

https://bugs.openjdk.org/browse/JDK-8021581


## 13 章 問題 33

私は並列処理が苦手なのですが、設問のプログラムは hello メソッド内でもう一方の Sample インスタンスでロックしようとしていないので、デッドロックは起きないように思います。設問の main メソッドはスレッドの終了を待たずに正常終了してしまうので、ExecutorService を使って、`shutdown()` の後に `awaitTermination(1L, TimeUnit.Days)` としてみたのですが、手元ではデッドロックは発生しませんでした。

可能性としては、次のようにしてプログラムが止まることはあると思いますが、これはロックの取得待ちで止まっているわけではないので、デッドロックではないはずです。

1. スレッド1が s1.hello() を実行し、s1.test が非 null になる
1. スレッド2がスレッド1が書き換えた s1.test を参照し、非 null なので、while ループを抜けられない
1. スレッド1が s1.bye() を実行し、スレッド1では s1.test が null に戻る
1. スレッド2が、s1.test が volatile でないのでスレッド1が更新した値を読むことができず、非 null の値を読み続けて while ループを抜けられない


## 最後に

合格には上記のような知識は必要ありませんが、どなたかのお役に立てれば幸いです。