日々常々

ふつうのプログラマがあたりまえにしたいこと。

recordのネスト、ついでに他も

record Outer(int a) {
    record Nest(int b) {
    }
}

こんなことしたらどうなるんだろうと気になりまして。

record が単にクラスのシンタックスシュガーであれば、NestはインナークラスになるのでOuterインスタンス変数、ここでは a にアクセスできるんですが、単なるネストクラスであればアクセスできません。 結論としてはNestからaにはアクセスできず、NestOuterインスタンスを持たない、staticが省略されていると言うことになります。

ネストだのインナーだのは 匿名クラスとかローカルクラスとか を参照ください。

確認はNestの方にメソッド生やしてOuterインスタンスメンバにアクセスしてみようとすればいいんですが、他にもNestjavapかリフレクションでコンストラクタやフィールドを見て、Outerがあるかどうかを見ればわかります。

そいや他でもどうなるんだろ?とこんなの書いて確認。

class OuterClass {
    class NestClass { }
    interface NestInterface { }
    enum NestEnum { }
    record NestRecord() { }
    static class StaticNestClass { }
    static interface StaticNestInterface { }
    static record StaticNestRecord() { }
    static enum StaticNestEnum { }
}

enum OuterEnum {
    CONSTANT;

    class NestClass { }
    interface NestInterface { }
    enum NestEnum { }
    record NestRecord() { }
    static class StaticNestClass { }
    static interface StaticNestInterface { }
    static record StaticNestRecord() { }
    static enum StaticNestEnum { }
}

public interface OuterInterface {
    class NestClass { }
    interface NestInterface { }
    enum NestEnum { }
    record NestRecord() { }
    static class StaticNestClass { }
    static interface StaticNestInterface { }
    static record StaticNestRecord() { }
    static enum StaticNestEnum { }
}

record OuterRecord(String s) {
    class NestClass { }
    interface NestInterface { }
    enum NestEnum { }
    record NestRecord() { }
    static class StaticNestClass { }
    static interface StaticNestInterface { }
    static record StaticNestRecord() { }
    static enum StaticNestEnum { }
}

で、全部のコンストラクタ舐めて出力。インタフェースはコンストラクタ無いから出ないけどね。

        String[] targets = {"Class", "Interface", "Enum", "Record"};

        record OuterAndNest(String outer, String nest) {
            Class<?> nestClass() {
                try {
                    return Class.forName(nest);
                } catch (ClassNotFoundException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        Arrays.stream(targets)
                .map(outer -> "Outer" + outer)
                .peek(System.out::println)
                .flatMap(outer -> Stream.concat(
                        Arrays.stream(targets).map(nest -> new OuterAndNest(outer, outer + "$Nest" + nest)),
                        Arrays.stream(targets).map(nest -> new OuterAndNest(outer, outer + "$StaticNest" + nest))
                ))
                .forEach(e -> {
                    Class<?> nestClass = e.nestClass();
                    System.out.println("  " + nestClass);
                    Arrays.stream(nestClass.getDeclaredConstructors())
                            .forEach(constructor -> {
                                System.out.println("    " + constructor);
                            });
                });

結果。

OuterClass
  class OuterClass$NestClass
    OuterClass$NestClass(OuterClass)
  interface OuterClass$NestInterface
  class OuterClass$NestEnum
    private OuterClass$NestEnum(java.lang.String,int)
  class OuterClass$NestRecord
    OuterClass$NestRecord()
  class OuterClass$StaticNestClass
    OuterClass$StaticNestClass()
  interface OuterClass$StaticNestInterface
  class OuterClass$StaticNestEnum
    private OuterClass$StaticNestEnum(java.lang.String,int)
  class OuterClass$StaticNestRecord
    OuterClass$StaticNestRecord()
OuterInterface
  class OuterInterface$NestClass
    public OuterInterface$NestClass()
  interface OuterInterface$NestInterface
  class OuterInterface$NestEnum
    private OuterInterface$NestEnum(java.lang.String,int)
  class OuterInterface$NestRecord
    public OuterInterface$NestRecord()
  class OuterInterface$StaticNestClass
    public OuterInterface$StaticNestClass()
  interface OuterInterface$StaticNestInterface
  class OuterInterface$StaticNestEnum
    private OuterInterface$StaticNestEnum(java.lang.String,int)
  class OuterInterface$StaticNestRecord
    public OuterInterface$StaticNestRecord()
OuterEnum
  class OuterEnum$NestClass
    OuterEnum$NestClass(OuterEnum)
  interface OuterEnum$NestInterface
  class OuterEnum$NestEnum
    private OuterEnum$NestEnum(java.lang.String,int)
  class OuterEnum$NestRecord
    OuterEnum$NestRecord()
  class OuterEnum$StaticNestClass
    OuterEnum$StaticNestClass()
  interface OuterEnum$StaticNestInterface
  class OuterEnum$StaticNestEnum
    private OuterEnum$StaticNestEnum(java.lang.String,int)
  class OuterEnum$StaticNestRecord
    OuterEnum$StaticNestRecord()
OuterRecord
  class OuterRecord$NestClass
    OuterRecord$NestClass(OuterRecord)
  interface OuterRecord$NestInterface
  class OuterRecord$NestEnum
    private OuterRecord$NestEnum(java.lang.String,int)
  class OuterRecord$NestRecord
    OuterRecord$NestRecord()
  class OuterRecord$StaticNestClass
    OuterRecord$StaticNestClass()
  interface OuterRecord$StaticNestInterface
  class OuterRecord$StaticNestEnum
    private OuterRecord$StaticNestEnum(java.lang.String,int)
  class OuterRecord$StaticNestRecord
    OuterRecord$StaticNestRecord()

わかりづらいから flatMap の後で適当に filter しとく。

                .filter(e -> Arrays.stream(e.nestClass().getDeclaredConstructors())
                        .anyMatch(constructor -> Arrays.stream(constructor.getParameterTypes())
                                .anyMatch(parameterType -> parameterType.getName().equals(e.outer()))))

結果。

OuterClass
  class OuterClass$NestClass
    OuterClass$NestClass(OuterClass)
OuterInterface
OuterEnum
  class OuterEnum$NestClass
    OuterEnum$NestClass(OuterEnum)
OuterRecord
  class OuterRecord$NestClass
    OuterRecord$NestClass(OuterRecord)

エンクロージングクラスのインスタンスを受けるようにコンストラクタが弄られるのは class の時だけ、と言うことで。

きっかけ

f:id:irof:20211022234826p:plain

IntelliJ IDEAさんが警告してくれなかったのよね。StaticNestInterfaceStaticNestEnumstatic要らないって灰色で、NestClassstaticにできるよって黄色で。 「あってもなくても意味ないのなら灰色になるんじゃないかなぁ、出ないってことは違いあるのかなぁ」って思ったのだけど、黄色にもなってないので。

f:id:irof:20211022235224p:plain

ああ、一律あってもなくても意味ないのに灰色にしてるわけじゃなく、「enumの不要な修飾子」みたいな括りなのか。なるほど。

f:id:irof:20211022235501p:plain

なおinterfaceにネストした場合は全部のstaticが灰色になってくれます。これはinterfaceのメンバに対する不要な修飾子で引っかかってる模様。

満足。(言語仕様とか見に行く気力は残ってない)

IntelliJ IDEAさんは IDEA-266665 でfix済だった。 2021.3 になったら期待してる通りになりそう。