4-1 基本型と参照型
4-1-1 はじめに
Javaの変数の方は基本型と参照型の2つに分類できる。この本では、よくある基本型からの説明ではなく、参照型から先に説明をしている。基本型を、「値を入れる箱」と説明する方法はよく見られるが、この方法からスタートすると誤った理解をさせてしまう恐れがあるから。この「値を入れる箱」モデルでは、参照型を理解するのに、「オブジェクトのアドレス」と「ポインタ型変数」の概念が必要になってくる。オブジェクト指向プログラミング言語のメリットは、この辺りを抽象化してくれているところにあるので、それでは本末点灯です。
そこで、この本では、「名なしのオブジェクトと(オブジェクトを)名前で呼ぶための変数」モデルで説明していっています。
4-1-2 オブジェクトと参照
- オブジェクト
- オブジェクトとはコンピュータのメモリ上に確保されたある領域。それ自体本質的に名前がありません。Javaではオブジェクトを名前で呼べるようにします。
- 変数
- 名なしオブジェクトを名前で呼べるための橋渡しをするのが変数です。変数とオブジェクトを結びつけて、(名前を持った)変数を通じて(名なしの)オブジェクトを操作できる、この概念こそが参照型変数を説明するモデル。
4-1-3 参照型変数とは
- 「参照」とはオブジェクトの位置情報を指し示す「何か」です
- 参照型変数は値として「参照」を持ちます
- 「参照」は、その直接の値を意識すべきではありません
- 結果として、参照型変数には「オブジェクトを指し示す役割」だけが残ります
4-1-7 変数名に使える文字と規約
コラムにコーディングスタイルの話がでてくる。
4-2 オブジェクト生成と代入
4-2-2 参照型変数の代入
- 参照の代入の動作
下のコードでは、sbとsb2は同じオブジェクトを参照する。
StringBuilder sb = new StringBuilder();
StringBuilder sb2 = sb;
4-2-5 null参照
StringBuilder sb = null;
これを「変数sbがnullを持つ」あるいは「変数sbがnullを参照する」と言う。個人的には、前者の言い方がしっくりくる。
- nullチェック
ヌルポを避けるためにnullチェックが必要。
if (sb != null) {
sb.insert(0, "[");
sb.append("]");
}
以下のようにも書ける。
if (Objects.nonNull(sb)) {
個人的には、Objectsクラスメソッドをもっと使ってもいいかと思っている。
例えば、文字列比較で以下のようなコードを書くことがあるが、
// s.equals("012")と書くのではなく
if ("012".equals(s)) {
// hoge
}
以下のように書くほうがいいと思う。
if (Objects.equals(sb, "012") {
// hoge
}
4-2-6 Optional型
Java8で導入されたOptional型。任意のオブジェクトをくるんでnullかもしれない状態を表現できる。Optional型を使うことにより、nullになりうる型と明示し、nullチェックを強制できる。
個人的には、まだOptional型の有効な使い道が見えていない。
Optional<StringBuilder> osb = Optional.of(sb)
または
Optional<StringBuilder> osb = Optional.ofNullable(sb)
で生成する。
取得は、
// 単純な取り出し
StringBuilder sb = osb.get();
// nullであれば、orElseの引数で渡したオブジェクを返す。
StringBuilder sb = osb.orElse(new StringBuilder("none"));
// nullであれば、orElseGetの引数に渡した関数の結果を遅延処理して返す
StringBuilder sb = osb.orElseGet(() -> new StringBuilder("none"));
// nullならなにもしない。nullでなければ処理を実行
osb.ifPresent(sb -> {
sb.insert(sb.inseert(0, "["));
sb.append("]");
});
// nullであれば空Optionalを返す
Optional<StringBuilder> os = osb.map(sb -> {
sb.insert(sb.inseert(0, "["));
sb.append("]");
});
- 多段のnullチェックのOptional型での書き変え
void showPersonFirstName(Person person) {
if (person != null) {
if (person.getName() != null) {
if (person.getName().getFirst() != null) {
System.out.println(person.getName().getFirst());
}
}
}
}
↓
class Name {
private final Optional<String> firsst;
Optional<String> getFirst() {
return this.first;
}
}
class Person {
private final Optional<Name> name;
Optional<Name> getName() {
return this.name;
}
}
void showPersonFirstName(Optional<Person> person) {
person.flatMap(Person::getName)
.flatMap(Name::getFirst)
.ifPresent(System.out::println);
}
そもそも多段の書き方はしないな。早期リターンを使うのが今までのやり方かな?
4-3 変数と型
変数の型は、その変数が参照するオブジェクトにたいしてどんな操作をできるかを確定します。
一方、オブジェクトの型は、呼ばれた先の実際の動作を確定します。
4-4 変数の詳細
4-4-1 変数の種類
- ローカル変数
- パラメータ変数(メソッドの仮引数)
- インスタンスフィールド変数
- クラスフィールド変数
- (配列構成要素)
4-4-3 変数のスコープ
変数の種類によってスコープの規則が違うことを説明している。スコープ内での変数の重複をシャドーイングということを初めて知った。
4-5 オブジェクトの寿命
4-6 final変数と不変オブジェクト
final変数は、再代入不可の変数。
final double PI = 3.14;
PI = 3.14159; // コンパイルエラー
4-6-1 参照型のfinal変数
final StringBuilder sb = new StringBuilder("012");
sb = new StringBuilder("345"); // 再代入でコンパイルエラー
// コンパイルエラーとはならない
final StringBuilder sb = new StringBuilder("012");
sb.append("345")
4-6-2 final変数と不変オブジェクトの動作の違い
変数の参照先オブジェクトそのものの内容の変更を禁止するには、開発者が自力で工夫する必要がある。
4-6-3 堅牢なプログラムのための工夫
- 変数は宣言と同時に初期化する
- 変数を使いまわさない
- nullのりよう範囲を限定する
- 変数のスコープを小さくする
- オブジェクトの寿命を意識する
- 不変オブジェクトを活用する