「Java」タグの記事が5件件あります
Javaプログラミング言語、フレームワーク、ベストプラクティス
全てのタグを見るJava Optional Best Practices
JavaのOptionalは、値が存在する場合も存在しない場合もあるコンテナオブジェクトです。これにより、nullチェックの手間を減らし、メソッドの戻り値として「値があるかどうか」を明示的に伝えることができます。Optionalは以下のような利点があります:
明確な意図: メソッドが値を返さない可能性があることを示す。 安全な操作: 明示的なnullチェックを回避し、NullPointerExceptionのリスクを減少させる。 関数型スタイル: map, filter, flatMap などを活用したチェーン処理が可能。
Optionalの作成
Optionalは主に2つのファクトリーメソッドを使って生成します。
- Optional.of(T value):
- 値が必ず非nullであることを期待します。
- nullを渡すと、NullPointerExceptionが発生します。
- Optional.ofNullable(T value):
- 値がnullの場合は空のOptional(Optional.empty())を返し、非nullの場合は値を保持します。
作成例:
// Method that processes an input string and returns an Optional.
public static Optional<String> processValue(String input) {
// Return empty if input is null or blank
if (input == null || input.trim().isEmpty()) {
return Optional.empty();
}
// Otherwise, return a trimmed value wrapped in an Optional
return Optional.of(input.trim());
}
Optionalの利用方法
Optionalの利用は主に以下のユースケースに分類されます。
-
値の存在チェック
- isPresent() / isEmpty():
- 値が存在するかどうかを確認する。
- 例: if (optional.isPresent()) { ... }
- isPresent() / isEmpty():
-
値の取得
- get():
- 値が存在する場合に返すが、存在しない場合はNoSuchElementExceptionをスローします。
- 注意: 直接get()を使うのはリスクがあるため、他のメソッドと組み合わせることが望ましい。
- orElse(defaultValue):
- 値が存在しない場合にデフォルト値を返します。
- orElseThrow(exceptionSupplier):
- Optionalが空の場合に、指定した例外をスローします。
- get():
-
値の処理
- ifPresent(Consumer):
- 値が存在する場合に、Consumer(値を受け取って何らかの処理を行うラムダ式)を実行します。
- ifPresentOrElse(Consumer, Runnable):
- 値が存在する場合はConsumerを、存在しない場合はRunnable(引数を取らない処理)を実行します。
- ifPresent(Consumer):
-
Optionalのチェーン処理
- map(Function):
- 値が存在する場合に、Functionを適用し結果を新しいOptionalとして返します。
- filter(Predicate):
- 値が条件を満たすかどうかをチェックし、満たさない場合は空のOptionalを返します。
- flatMap(Function):
- ネストされたOptionalの解除に利用します。
- or(Supplier):
- Optionalが空の場合に、新たなOptionalを返すために利用します。
- map(Function):
利用例:
String value1 = " Hello, World! ";
Optional<String> opt1 = processValue(value1);
// 存在チェック
if (opt1.isPresent()) {
System.out.println("Value exists!");
} else {
System.out.println("Value is empty.");
}
// 値の取得
try {
// 値がなければ例外がスローされる
String result = opt1.orElseThrow(() -> new IllegalStateException("Value is empty"));
System.out.println("Result: " + result);
} catch (IllegalStateException ex) {
System.err.println(ex.getMessage());
}
// デフォルト値を利用
String defaultResult = opt1.orElse("Default Value");
System.out.println("Default Result: " + defaultResult);
// 値の処理
opt1.ifPresent(val -> System.out.println("Processed value1: " + val));
// ifPresentOrElseを使用して、存在する場合は処理し、存在しない場合は別の処理を実行
opt1.ifPresentOrElse(
val -> System.out.println("Processed value1: " + val),
() -> System.out.println("Value is empty")
);
// チェーン処理:変換とフィルタ
Optional<Integer> lengthOpt = opt1
.map(String::toUpperCase) // 値を大文字に変換
.filter(val -> val.length() > 10) // 長さが10より大きいかフィルター
.map(String::length); // 文字列の長さを取得
lengthOpt.ifPresent(len -> System.out.println("Length of processed value: " + len));
// orを使用して、Optionalが空の場合に代替値を提供
Optional<String> opt2 = opt1.or(() -> Optional.of("Default Greeting"));
System.out.println("Result from opt2: " + opt2.get());
Optionalのベストプラクティス
-
戻り値としての利用:
- Optionalはメソッドの戻り値に利用し、値が存在しない可能性を明示する。
-
メソッド引数としては使用しない:
- Optionalを引数にすると、呼び出し側に無駄なラッピングを強いるため、シンプルなnullチェックやオーバーロード、あるいは別のパラメータ設計を検討する。
-
直接get()の使用を避ける:
- 値の取得はorElse, orElseThrow, ifPresentなどのメソッドを利用して、安全に扱う。
-
不必要なオブジェクト生成を避ける:
- チェーン操作を利用して、コードをシンプルに保つ。
Java Development Setup, SDKMAN, Gradle, VS Code
SDKMAN! is a popular Java version manager for developers. It allows you to easily install, switch between, and manage multiple Java versions and development tools.
Install SDKMAN!
# install
curl -s "https://get.sdkman.io" | bash
# init
source "$HOME/.sdkman/bin/sdkman-init.sh"
Install Java
# list available Java versions
sdk list java
# Install Java 21
sdk install Java 21.0.1-tem
# Set as default
sdk default java 21.0.1-tem
# Verify installation
java -version
Fix JAVA_HOME Environment Variable
SDKMAN! sometimes doesn't set JAVA_HOME automatically. Add this to your shell configuration:
# For zsh (default on newer macOS)
echo 'export JAVA_HOME="$SDKMAN_DIR/candidates/java/current"' >> ~/.zshrc
echo 'export PATH="$JAVA_HOME/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc
# Verify JAVA_HOME is set
echo $JAVA_HOME
Install Gradle
sdk install gradle
gradle --verion
Install Spring boot
sdk install springboot
Start coding Java with VSCode
Install the Extension Pack for Java which includes 7 essential extensions:
- Language Support for Java
- Debugger for Java
- Test Runner for Java
- Maven for Java
- Project Manager for Java
- Visual Studio IntelliCode
- Gradle for Java
Update user settings: Command + Shift + P > Open user settings (JSON) with the following settings:
{
// Java Runtime Configuration
"java.jdt.ls.java.home": "${env:JAVA_HOME}",
"java.configuration.runtimes": [
{
"name": "JavaSE-21",
"path": "${env:JAVA_HOME}",
"default": true
}
],
// Java Language Server Settings
"java.configuration.detectJdksAtStart": true,
"java.configuration.updateBuildConfiguration": "automatic",
"java.server.launchMode": "Standard",
"java.autobuild.enabled": true,
"java.maxConcurrentBuilds": 1,
// Code Completion and Analysis
"java.completion.enabled": true,
"java.completion.guessMethodArguments": true,
"java.completion.favoriteStaticMembers": [
"org.junit.jupiter.api.Assertions.*",
"org.mockito.Mockito.*",
"org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*",
"org.springframework.test.web.servlet.result.MockMvcResultMatchers.*"
],
"java.compile.nullAnalysis.mode": "automatic",
"java.sources.organizeImports.staticStarThreshold": 5,
"java.sources.organizeImports.starThreshold": 5,
// Code Formatting
"java.format.enabled": true,
"java.format.settings.url": "https://raw.githubusercontent.com/google/styleguide/gh-pages/eclipse-java-google-style.xml",
"java.format.settings.profile": "GoogleStyle",
"java.format.comments.enabled": true,
// Gradle Settings
"java.import.gradle.enabled": true,
"java.import.gradle.wrapper.enabled": true,
"java.import.gradle.offline.enabled": false,
"java.import.gradle.arguments": "--refresh-dependencies",
"gradle.javaDebug.cleanOutput": true,
"gradle.debug": false,
// Testing
"java.test.defaultConfig": "",
"java.test.config": {},
// Debugging
"java.debug.logLevel": "warn",
"java.debug.settings.enableRunDebugCodeLens": true,
"java.debug.settings.showHex": false,
"java.debug.settings.showStaticVariables": false,
"java.debug.settings.showQualifiedNames": false,
"java.debug.settings.maxStringLength": 0,
"java.debug.settings.exceptionBreakpoint.skipClasses": [
"$JDK",
"junit.*",
"org.junit.*",
"org.springframework.*"
],
// Code Lens
"java.references.includeAccessors": true,
"java.references.includeDecompiledSources": true,
"java.implementationsCodeLens.enabled": true,
"java.referencesCodeLens.enabled": true,
// Error Reporting and Telemetry
"java.errors.incompleteClasspath.severity": "warning",
"java.telemetry.enabled": false,
// Performance
"java.jdt.ls.vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx2G -Xms100m",
// Code Completion Filtering (keep packages you don't commonly use out of suggestions)
"java.completion.filteredTypes": [
"java.awt.*",
"com.sun.*",
"sun.*",
"jdk.*",
"org.graalvm.*",
"io.micrometer.shaded.*",
"javax.swing.*"
],
// Spring Boot specific (useful for microservices)
"spring-boot.ls.checkjvm": false,
"spring.initializr.defaultLanguage": "Java",
"spring.initializr.defaultJavaVersion": "21",
"spring.initializr.defaultPackaging": "Jar",
}
Java Sealed
After Java 8, the interface of java have become more and more powerful.
- (Java 8) Default methods Provide optional functionality to all implementations
- (Java 8) Static methods Utility methods that belong to the interface contract itself, CANNOT be overridden. utility functions that don't need instance context
- (Java 9) Private methods: keeping internal helper logic hidden from implementers
- Constants: Shared Contract Values public static final cannot be overriden
- (Java 17) Sealed: Sealed means you can control exactly which classes can implement your interface (or extend your class). closed hierarchy, This enables better pattern matching, safer APIs, and clearer domain modeling!
- (Java 8) @FunctionalInterface indicate this interface supports lambda.
Example:
public interface PaymentProcessor {
// Abstract method - must be implemented
void processPayment(double amount);
// Default method - optional to override
default void logTransaction(double amount) {
System.out.println("Processing payment of: $" + amount);
// Common logging logic here
}
// Another default method
default boolean validateAmount(double amount) {
return amount > 0 && amount < 10000;
}
}
public abstract class AbstractPaymentProcessor {
private int defaultAmount = 500;
// Abstract method - must be implemented
void processPayment(double amount);
// Default method - optional to override
void logTransaction(double amount) {
System.out.println("Processing payment of: $" + amount);
// Common logging logic here
}
// Another default method
boolean validateAmount(double amount) {
return amount > 0 && amount < 10000;
}
}
Java Interface powerful features
After Java 8, the interface of java have become more and more powerful.
- (Java 8) Default methods Provide optional functionality to all implementations
- (Java 8) Static methods Utility methods that belong to the interface contract itself, CANNOT be overridden. utility functions that don't need instance context
- (Java 9) Private methods: keeping internal helper logic hidden from implementers
- Constants: Shared Contract Values public static final cannot be overriden
- (Java 17) Sealed: Sealed means you can control exactly which classes can implement your interface (or extend your class). closed hierarchy, This enables better pattern matching, safer APIs, and clearer domain modeling!
- (Java 8) @FunctionalInterface indicate this interface supports lambda.
Example:
public interface PaymentProcessor {
// Abstract method - must be implemented
void processPayment(double amount);
// Default method - optional to override
default void logTransaction(double amount) {
System.out.println("Processing payment of: $" + amount);
// Common logging logic here
}
// Another default method
default boolean validateAmount(double amount) {
return amount > 0 && amount < 10000;
}
}
public abstract class AbstractPaymentProcessor {
private int defaultAmount = 500;
// Abstract method - must be implemented
void processPayment(double amount);
// Default method - optional to override
void logTransaction(double amount) {
System.out.println("Processing payment of: $" + amount);
// Common logging logic here
}
// Another default method
boolean validateAmount(double amount) {
return amount > 0 && amount < 10000;
}
}