Javaの勉強

クラス
アクセス修飾子
PHPと違ってクラスにもアクセス修飾子がつく。
メンバ変数には広い順にpublic、protected、修飾子なし、privateの4つが存在するが、クラスにつけられるのはpublicと修飾子なしのパターンのみ。
protectedは同一パッケージやサブクラスからアクセス可能。
修飾子なしでは同一パッケージからアクセス可能であるが、サブクラスからアクセスできない。
privateは同一クラス内のみ。
ただし、内部クラスの場合はprotectedやprivateの外部クラスにアクセスすることが可能。つまり、これは内部クラスは外部クラスの一部であり、内部クラスの場合はメンバに近い性質として扱われる。
内部クラス
クラスの内部に定義されたクラス。外側を外部クラス、内側で定義されたものを内部クラスと呼ぶ。
スーパークラスから継承したサブクラスとは異なる概念である。
内部クラスは外部クラスがインスタンス化されていることを前提に、外部クラスを通じてインスタンス化する必要がある(クラスのメソッドを呼び出す感覚)。
ただ、一度インスタンス化してしまうと外部クラスと独立して参照できる。
内部クラスから外部クラスについてはアクセス修飾子の制約を受けないが、外部クラスがprotectedやprivateな内部クラスを見ることはできない。
InnerClass innerObj = outerObj.new InnerClass(200);
innerObj.display();
①非静的内部クラス
- メソッドや変数と同じくインスタンス化されていないと利用できない。
- 内部クラスがインスタンス化されているときは外部クラスがインスタンス化されているので内部から外部は見えるが、逆はそうとは言えないため外部から内部は見えない。内部クラスを使用するには明示する必要がある。
▼ 非静的内部クラスのインスタンス生成
Outer.Inner inner = (new Outer()).new Inner();
②静的内部クラス
③ローカル内部クラス
- メソッド内に存在するクラス
- メソッド内の変数と似たような性質で、protectedやprivateなどのアクセス修飾子をつけない。

Optional型
値が存在しない可能性があることを示す型。
Swiftの場合は値があるか、ないかの2つの状態が存在する。アンラップという処理を行った結果、値がある場合とない場合(nil)で処理を分岐させる。
var optionalValue: Int? = 10
optionalValue = nil
// if let文によるOptional型の値のアンラップ
if let value = optionalValue {
print("Value is \(value)")
} else {
print("Value is nil")
}
Javaの場合はPresent(値が存在する)とEmpty(値が存在しない)が存在し、isPresent()
メソッドの結果からPresentであるかどうかを判定できる。Swiftはif let文でアンラップと値の取り出しを1つのステートメントで行うが、JavaのOptionalの場合はPresentのほかにget()
のような値を取り出すメソッドが存在する。
また、Swiftのnil合体演算子のようにorElse(T other)
メソッドを使うことでOptional型に値がなかった場合のデフォルト値を取得できる。

ラムダ式
C#やJavaなどに登場する関数を式として表す構文。他の言語だと無名関数と呼ばれるものが近い。
関数型インターフェースの実装において使われる。
関数型インターフェース
インターフェースというのはJavaにおけるインターフェースを指しており、関数型インターフェースは以下のように@FunctionalInterfaceアノテーションをつけて単一の抽象メソッドとともに宣言する。
package java.util.function;
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
上記はJava組み込みのConsumerと呼ばれるインターフェースであり、以下のようにインターフェースを実装する。
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// Consumerインターフェースの実装
Consumer<Integer> doubleConsumer = (Integer number) -> {
System.out.println(number * 2);
};
// リストの各要素にConsumerを適用
numbers.forEach(doubleConsumer);
}
上記ではdoubleConsumerと呼ばれるConsumerインターフェースの実装を定義しているが、これは引数を1つ受け取り値を返さない(戻り値がvoid)ラムダ式であることが約束されている。
Consumer<T>
引数を1つ受け取って値を返さないインターフェース。
Supplier<T>
引数を受け取らずに値を返すインターフェース。
Function<T, R>:
引数を1つ受け取り結果を返すインターフェース。
Predicate<T>
引数を1つ受け取りbool型の結果を返すインターフェース。
UnaryOperator<T>
引数で受け取った型と同じ型の結果を返すインターフェース。

コンストラクタと静的ファクトリメソッド
- クラスからオブジェクトをnewするとコンストラクタが呼び出されてオブジェクトの初期化が行われる。そして新しいインスタンスが生成される。
- このとき同一インスタンスが作成されるのが一度であることを保証し、一度インスタンスを生成した後は同じインスタンスを使い回したい場面がある(シングルトンパターン)
- その際はクラスに持たせた静的メソッド経由でコンストラクタを呼び出し、インスタンスが存在しない場合は新しく生成されたインスタンスを返し、既に存在する場合は既存のものを返すように実装する(ファクトリメソッドパターン)
シングルトンの実現パターン
静的メソッドに持たせるパターン
public class Logger {
private static Logger instance;
private Logger() {
// 初期化ロジック
}
public static Logger getInstance() {
if (instance == null) {
instance = new Logger();
}
return instance;
}
public void log(String message) {
System.out.println(message);
}
}
インスタンスを静的フィールドに持たせるように設計し、呼び出されたときは常に同じインスタンスを返す。
インスタンスをキャッシュするパターン
public class DatabaseCache {
private static Map<String, String> cache = new HashMap<>();
public static String getData(String query) {
if (!cache.containsKey(query)) {
// データベースからデータを取得(シミュレーション)
String result = "Result of " + query;
cache.put(query, result);
}
return cache.get(query);
}
}
シングルトンと似ているが、シングルトンがアプリケーションで1つのインスタンスを管理する思想なのに対して、キャッシュはキーと値の組み合わせを管理しておくものとなっている。
スレッドセーフで管理するパターン
import java.util.concurrent.ConcurrentHashMap;
public class ObjectFactory {
private static ConcurrentHashMap<String, MyObject> cache = new ConcurrentHashMap<>();
public static MyObject getObject(String key) {
// キャッシュからインスタンスを取得、存在しない場合は作成してキャッシュに保存
return cache.computeIfAbsent(key, k -> new MyObject(k));
}
}
class MyObject {
private String value;
public MyObject(String value) {
this.value = value;
}
// その他のメソッドやフィールド
}