1. キーワード var
Java でコードを書き始めると、厳密な型システムに出会います。各変数は明示的に宣言し、型を指定しなければなりません。これは信頼性のために有用ですが、時に記述が冗長になりコードを読みづらくします。そこで開発を楽にするために、Java には便利な仕組み — 型推論(var) が加わりました。
var を使うと、型安全性を損なうことなく、より短くクリーンなコードが書けます。右辺に値を書けば、コンパイラが型を推論してくれます。
例
var age = 23; // コンパイラが 23 の型を推論し、int age を挿入する
var name = "Anna"; // コンパイラが "Anna" の型を推論し、String name を挿入する
var price = 99.99; // コンパイラが 99.99 の型を推論し、double price を挿入する
なぜ var が導入されたのか?
- 読みやすさ: HashMap<String, List<Integer>> のような長い型を手で書く必要がありません。
- 柔軟性: 右辺を変更すれば、左辺の var は自動で追随します。
- 現代的なスタイル: 初期化から型が容易に読み取れる箇所では、ほとんどの現代的な Java プロジェクトが var を使用しています。
var を使ってよい(推奨される)場面
- 右辺から変数の型が一意に分かるとき。
- 型が明らかなとき(var price = 100; — これは int と分かる)。
- 型が長すぎる・複雑なとき(例: HashMap<Integer, List<Calendar>>)。
配列の例
var numbers = new int[] { 1, 2, 3, 4 };
メソッドの例
var input = console.nextLine(); // input は String(メソッドは String を返す)
var を使わない方がよい場合
右辺から型が明確でないと、コードの可読性が下がることがあります。1か月後に自分で見返して型が分からないようなら、明示的に書いた方がよいでしょう。
var mystery = DoSomethingVeryComplicated(); // これは何の型だ、mystery???
ここでは明示的に型を書くのがよいでしょう:
String result = DoSomethingVeryComplicated();
黄金律: var は可読性を損なわないところだけで使う!
var はローカル変数専用
キーワード var はメソッド内のローカル変数でのみ使えます。次の用途では使えません:
- メソッドのパラメータ;
- クラスのフィールド;
- 定数。
var の仕組み(内部的には)
- コンパイル時に Java コンパイラが正しい型を挿入します。
- コンパイル後、var は存在しません。
- 性能低下はありません。あくまでプログラマのための記述上の利便性です。
var year = 2025; // コンパイル時には次のようになる: int year = 2025;
var の典型的な誤り
初期化していない変数:
var a; // エラー: 型を推論する材料がありません!
あいまいな初期化:
var list = null; // エラー: null の型は定義できません!
異なる型での再利用:
var value = 5; // value は int
value = "Five"; // エラー: value はすでに int です!
比較: 明示的な型と var
| シナリオ | 明示的な型 | var |
|---|---|---|
| 型が明確 | |
|
| メソッドが複雑な型を返す | |
|
| 配列 | |
|
2. キーワード final: 定数の宣言
定数とは、初期化後に値を変更できない変数です。定数はコードを堅牢かつ分かりやすくし、重要な数値や文字列の偶発的な変更から守ってくれます。
- 円周率(PI = 3.14159…)
- 最大ユーザー数(MAX_USERS = 1000)
- 会社名(COMPANY_NAME = "CodeGym")
Java では定数の宣言に final 修飾子を使います。コンパイラに「最初の代入以降は変更不可!」と伝えるものです。
final Tip IMYA_KONSTANTY = znachenie;
構文
final int DAYS_IN_WEEK = 7;
final double PI = 3.1415926535;
final String GREETING = "こんにちは、Java!";
重要なポイント:
- final の変数は必ず初期化が必要です(クラスのフィールドであれば、フィールド初期化子やコンストラクタで)。
- 値を変更しようとするとコンパイルエラーになります。
例: final 変数は変更できない
final int MAX_USERS = 100;
MAX_USERS = 200; // エラー: final 変数に新しい値は代入できません
補足: 代入をコメントアウト解除すると、プロジェクトはビルドに失敗します。
3. static final: クラスレベルの定数
Java では、特定のオブジェクトに依存しない「クラス全体で共有する」定数がよく必要になります。そのために static + final の組み合わせを使います。
なぜ static final なのか?
- static — 変数はクラスに属し(全インスタンスで 1 つ)。
- final — 値は変更できません。
構文
public static final double PI = 3.1415926535;
public static final int MAX_USERS = 1000;
public static final String COMPANY_NAME = "CodeGym";
どこに宣言する? 通常はクラスの先頭(メソッドより前)に置きます。
クラス内での使用例
public class MathUtils
{
public static final double PI = 3.1415926535;
public static double circleLength(double radius)
{
return 2 * PI * radius;
}
}
呼び出し:
double len = MathUtils.circleLength(5);
System.out.println(len); // 31.4159...
注意: 定数にはクラス名経由でアクセスします: MathUtils.PI。
4. 単なる final と static final の違い
final — フィールドは初期化後に値を変更できませんが、各オブジェクトごとに値を持ちます。
static final — クラス全体で 1 つの定数であり、オブジェクトには依存しません。
実例: 違いを確認
public class User
{
public final String name; // 各 User が独自の名前を持つ(final)
public static final String COMPANY = "CodeGym"; // 全員で共有される 1 つの会社名(static final)
public User(String name)
{
this.name = name;
}
}
User u1 = new User("Vasya");
User u2 = new User("Petya");
System.out.println(u1.name); // Vasya
System.out.println(u2.name); // Petya
System.out.println(User.COMPANY); // CodeGym
結論: name は各インスタンス固有の不変フィールド、COMPANY は共有の定数です。
定数の命名規約
定数名は大文字+アンダースコア(UPPER_SNAKE_CASE)で記述するのが慣例です。
public static final int MAX_USERS = 1000;
public static final double GRAVITY = 9.81;
public static final String DEFAULT_GREETING = "Hello, world!";
ヒント: このような名前を見かけたら、値を変更しようとしないでください。コンパイラが許しません。
5. 実践: 定数の宣言と利用
例: 定数を持つクラス
public class CircleCalculator
{
public static final double PI = 3.1415926535;
public static double getLength(double radius)
{
return 2 * PI * radius;
}
public static double getArea(double radius)
{
return PI * radius * radius;
}
}
使用例:
public class Main
{
public static void main(String[] args)
{
double r = 5.0;
System.out.println("円周の長さ: " + CircleCalculator.getLength(r));
System.out.println("円の面積: " + CircleCalculator.getArea(r));
}
}
実行結果:
円周の長さ: 31.415926535
円の面積: 78.5398163375
定数を変更しようとした例
CircleCalculator.PI = 3.14; // コンパイルエラー: cannot assign a value to final variable PI
6. 補足と注意点
オブジェクト参照に対する定数
オブジェクト参照にも final を付けられますが、これ自体はオブジェクトを不変にしません。あくまで、その変数に別の参照を代入できなくするだけです。
final StringBuilder sb = new StringBuilder("Hello");
sb = new StringBuilder("World"); // エラー: 新しいオブジェクトを代入できません
sb.append(", Java!"); // 許可される — オブジェクトの状態が変わるだけ
System.out.println(sb); // Hello, Java!
結論: final が守るのは参照であって、オブジェクト自体ではありません。本当に不変にしたい場合は、不変クラス(例: String、List.of() など)を使いましょう。
マジックナンバーの代わりに定数を使う
悪い例:
double area = 3.1415 * r * r; // 3.1415 とは何?なぜこの数?
良い例:
double area = PI * r * r; // 円周率を使っているとすぐ分かる
7. 定数と var を扱う際のよくあるミス
エラー1: final 変数の値を変更しようとする。 変数が final で宣言されている場合、再代入はコンパイルエラーになります。
エラー2: var で定数を宣言しようとする。 定数には常に明示的な型と final または static final を使います。var はローカル変数専用です。
エラー3: 定数の命名が不適切。 定数名は UPPER_SNAKE_CASE(大文字+アンダースコア)で記述します。final int maxUsers = 100; のような記法は可読性を下げます。
エラー4: スコープを忘れる。 メソッド内で宣言した定数はそのメソッド内でしか見えません。クラス全体で使う場合は、static final としてクラスレベルに宣言します。
エラー5: final でオブジェクトが不変になると思い込む。 final が固定するのは参照であり、オブジェクトの状態ではありません — この 2 つは別物です。
GO TO FULL VERSION