Javaの小ネタ、Tips

# 概要
徒然なるままに書き綴ってみるだけです。役に立つかどうかは知りません。また、必ずしもJava固有の話ではないと思います。

## 内容
### 多重代入
複数の変数に同じ値を代入することができます。

```java
int x, y;
x = y = 10;
System.out.println(x); // 10
System.out.println(y); // 10
```

これは、下記のように解釈されるっぽいです。yに10が代入されますが、Javaでは代入式も値が返りますから、 `(y = 10)` 全体の評価値がxに代入されるということですね。

```java
x = (y = 10);
```

## mainメソッドの書き方
mainメソッドは一般的には下記のように書くと思います。

```java
public static void main(String[] args) {
    System.out.println("Hello world!");
}
```

Java5以降なら下記のように書いてもOKです。

```java
public static void main(String... args) {
    System.out.println("Hello world!");
}
```
`String[]` か `String...` の違いだけです。前者は配列で後者は可変長引数ですが、mainメソッドはどちらでもいいようです。

## ただのスコープ
if文やfor文などを使うと新しいスコープが作られますが、条件分岐やループなどをしない単なるスコープを作ることもできます。

```java
{
    int x = 10;
    System.out.println(x);  // 10
}

{
    int x = 100;
    System.out.println(x); // 100
}

// System.out.println(x); // コンパイルエラー
```

`x` が2回定義されていますが、スコープが別なのでコンパイルエラーにはなりません。また、`x` のスコープはブレースで囲った範囲だけなので、ブレースの外で `x` を参照しようとするとコンパイルエラーです。

## HashSetの実装
HashSetは、実装としてはただHashMapをラップしているだけのクラスです。
（※実装の詳細の話なので、将来変更される可能性はあります）

```java
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
```

```java
    public boolean remove(Object o) {
        return map.remove(o)==PRESENT;
    }
```

## AbstractStringBuilder
`StringBuilder` や `StringBuffer` はJavaDocではObjectクラス直下のサブクラスであるかのように書かれていますが、実際には `AbstractStringBuilder` のサブクラスです。
`AbstractStringBuilder` はパッケージプライベート（公開APIではない）のでJavaDocには書かれてないのかもしれません。
（※実装の詳細の話なので、将来変更される可能性はあります）

```java
public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, Comparable<StringBuilder>, CharSequence
{
// 後略
```

```java
 public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, Comparable<StringBuffer>, CharSequence
{
// 後略
```

## メソッド内でメソッドを定義する
通常はJavaではメソッド内にメソッドを定義することはできません。しかし、Java10で導入された `var` を使えばそれっぽいことができます。（別の記事に書いたことがありますが）

```Main.java
public class Main {
    public static void main(String[] args) {
        // func.capitalize() で呼び出せる
        // func はローカル変数なので、このメソッドの外からは呼べない
        var func = new Object() {
            private String capitalize(String s) {
                return s.substring(0, 1).toUpperCase() + s.substring(1, s.length()).toLowerCase();
            }
        };

        System.out.println(func.capitalize("tokyo")); // => Tokyo
        System.out.println(func.capitalize("TOKYO")); // => Tokyo
    }
}
```

## Javaのソースコードをコンパイルなしで実行する
これも別の記事に書きましたが、Java11以降は `javac` を使わず `java` コマンドでソースファイルを指定して実行させることができます。ちょっとした動作確認のために便利かもしれません。
まあ、本当にコンパイルなしで実行するわけではなくて裏でコンパイルしているのだろうと思いますが。

## 既存クラスのメソッドに処理を埋め込む
Javaでは匿名クラスを使って既存のメソッドに処理を埋め込むことができます。

```java
List<String> list = new ArrayList<>() {
    @Override
    public boolean add(String s) {
        System.out.println("add called. arg: " + s);
        return super.add(s);
    }
};
list.add("hello"); // add called. arg: hello
list.add("world"); // add called. arg: world
System.out.println(list); // [hello, world]
```

## `null` に対して `instanceof` を使う
`null` に対して `instanceof` を使うことができます。

```java
System.out.println(null instanceof Object); // false
```

`true` にならないのは当然ですが、 `NullPointerException` が投げられないというのが注目ポイントかと思います。 `null` が代入されている変数でも同じ結果になりますので、 `instanceof` の前に `null` チェックをしなくて済みます。

## `null` を `throw` する
例外型の変数に `null` が入っていた場合、 `throw` すると変数の型に関係なく `NullPointerException` が投げられます。

```java
RuntimeException e = null;
throw e; // NullPointerException
```

## `null` をキャストする
あまり意味がないですが、とりあえず何の例外も投げられません。

```java
Object obj = null;
String str = (String)obj;
System.out.println(str); // null
```

## サブクラスを許容しない型判定
`instanceof` を使うと、インスタンスの型が指定したクラスのサブクラスの場合も `true` となります。

```java
System.out.println("str" instanceof Object); // true
```

サブクラスの場合は `false` にしたいなら下記のように `Class` 型同士を `equals` で比較するといいでしょう。

```java
System.out.println(new Object().getClass().equals(Object.class)); // true
System.out.println("str".getClass().equals(Object.class)); // false
```

## 配列は型安全ではない
配列は、ある型の配列と、その型のサブクラスの配列に代入互換性があります。
そのため、下記のようなコードがコンパイルされてしまい、実行時にエラーとなります。

```java
Object[] array = new String[3];
array[0] = new Integer(10); // ArrayStoreException
```

ジェネリクスを使ったListであれば、コンパイルエラーになるため上記のような問題は発生しません。

```java
List<Object> list = new ArrayList<String>(); // incompatible types: ArrayList<String> cannot be converted to List<Object>
```

## オーバーフローを検知する
演算時にオーバーフローが発生すると、何のエラーも出ずにただ不正な計算結果になります。
`Math` クラスのメソッドで、オーバーフローが発生した際に例外を投げるユーティリティーメソッドがあるため、その例外を捕捉することでオーバーフローを検知できます。

```java
System.out.println(Integer.MAX_VALUE + 1); // -2147483648
Math.addExact(Integer.MAX_VALUE, 1); // ArithmeticException: integer overflow
```

まあ、最初から `BigInteger` を使えばいいような気もしますけれど。

# 終わりに
何か一つでも参考になる項目があれば幸いです。
他に何か思いついたら追記するかもしれませんし、しないかもしれません。