Java Academy · Oppitunti

Optionalin parhaat käytännöt

Sovella metodeja orElse, orElseGet ja orElseThrow sekä vältä yleisiä Optionalin vastamalleja.

Oppitunti 4/413 vaihetta

Optionalin parhaat käytännöt on ilmainen Java Academy-oppitunti CoddyKitissä. Tämä on oppitunti 4/4. Voit lukea koko oppitunnin alta ilmaiseksi ja harjoitella sen jälkeen käytännössä selaimessa sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla. Oppitunti kuuluu Java Academy-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. Java Academy-kurssilla on yhteensä 4 oppituntia.

Optional-tyypin parhaat käytännöt

Optional on tehokas työkalu, kun sitä käytetään oikein. Tässä oppitunnissa käsitellään sen käyttöä koskevia suosituksia ja kieltoja käytännön esimerkkien avulla oikeista koodikannoista.

Toimikaa näin: käyttäkää Optional-tyyppiä palautustyyppinä

Optional-tyypin ensisijainen käyttötapa on niiden metodien palautustyyppinä, jotka eivät välttämättä löydä tai tuota arvoa.

import java.util.Optional;

// Good: signals to caller that result might be absent
public Optional<User> findByUsername(String username) { ... }
public Optional<Config> getConfig(String key) { ... }
public Optional<String> getHeader(String name) { ... }

// These are all cases where absence is a normal outcome,
// not an exceptional condition

Älkää tehkö näin: Optional kenttänä

Optional-kentät lisäävät olioiden kustannuksia ja vaikeuttavat sarjallistamista. Käyttäkää luokkien sisäisissä valinnaisissa kentissä null-arvoa tai sentineliarvoa.

// BAD
class User {
    private Optional<String> middleName; // wrong
}

// GOOD: use null for absent optional field
class User {
    private final String middleName; // null = absent
    public Optional<String> middleName() {
        return Optional.ofNullable(middleName); // expose as Optional in getter
    }
}
User u = new User(null); // null field
System.out.println(u.middleName().isPresent()); // false

Älkää tehkö näin: Optional metodin parametrina

Optional-parametrit pakottavat kutsujat käärimään arvot tarpeettomasti Optional-olioihin. Käyttäkää sen sijaan ylikuormitusta tai @Nullable-annotaatiota.

// BAD
void sendNotification(String message, Optional<String> subject) { ... }
// caller must write: sendNotification("Hello", Optional.of("News"))

// GOOD: overloading
void sendNotification(String message) { sendNotification(message, null); }
void sendNotification(String message, String subject) {
    // subject may be null
}
// caller writes: sendNotification("Hello", "News") — clean

Älkää tehkö näin: Optional kokoelmissa

Älkää koskaan sijoittako Optional-olioita Listan tai Setin sisään tai Mapin arvoksi. Tyhjillä Optional-olioilla kokoelmissa ei ole merkitystä — jättäkää alkio yksinkertaisesti pois.

// BAD: List of Optionals
List<Optional<User>> results = ...; // meaningless empty Optionals

// GOOD: List of present values only
List<User> presentUsers = maybeUsers.stream()
    .flatMap(Optional::stream)
    .collect(Collectors.toList());

// BAD: Map with Optional values
Map<String, Optional<String>> config = ...;

// GOOD: just use null or omit the key for absent values
Map<String, String> config2 = ...;
String value = config2.getOrDefault("key", "default");

Toimikaa näin: orElseGet kalliille oletusarvoille

Kun oletusarvo edellyttää laskentaa (tietokantakysely tai verkkokutsu), käyttäkää aina orElseGet-metodia — toimittajaa kutsutaan vain tarvittaessa.

import java.util.Optional;

// BAD: buildDefault() always called, even when value is present!
String config = getConfig("timeout").orElse(buildDefault());

// GOOD: supplier called lazily only when empty
String config2 = getConfig("timeout").orElseGet(() -> buildDefault());

// Or with method reference:
String config3 = getConfig("timeout").orElseGet(this::buildDefault);

// For cheap literals, orElse is fine
String name = getConfig("name").orElse("default");

Toimikaa näin: orElseThrow pakollisille arvoille

Kun puuttuva arvo merkitsee ohjelmointivirhettä tai invariantin rikkomista, käyttäkää orElseThrow-metodia kuvailevan poikkeuksen kanssa.

import java.util.Optional;

// Good: throw when absence is an error
User user = userRepository.findById(userId)
    .orElseThrow(() -> new UserNotFoundException("User not found: " + userId));

// Good: in validation code
String apiKey = Optional.ofNullable(System.getenv("API_KEY"))
    .orElseThrow(() -> new IllegalStateException(
        "API_KEY environment variable is required"));

System.out.println("Key found: " + apiKey.length() + " chars");

Vastaesimerkki: isPresent + get()

Välttäkää vastaesimerkkiä, jossa tarkistetaan ensin isPresent() ja kutsutaan sitten get()-metodia. Tämä ei ole null-tarkistusta parempi ja vesittää Optional-tyypin tarkoituksen.

import java.util.Optional;

Optional<String> opt = findValue();

// BAD: isPresent() + get() — no improvement over null check
if (opt.isPresent()) {
    String val = opt.get();
    process(val);
}

// GOOD: use the functional API
opt.ifPresent(this::process);

// GOOD: transform and return
return opt.map(this::process).orElse(null);

// GOOD: provide a default
String result = opt.orElse("default");

Ketjutuksen paras käytäntö

Rakentakaa selkeä ketju: etsi → suodata → muunna → käytä varavaihtoehtoa. Jokainen vaihe kertoo itse, mitä se tekee.

import java.util.Optional;

record User(long id, String plan, boolean active) {}

static Optional<User> findUser(long id) { return Optional.empty(); }

String result = findUser(42L)
    .filter(User::active)           // keep only active users
    .filter(u -> "PRO".equals(u.plan())) // keep only PRO users
    .map(u -> "Welcome, Pro user #" + u.id()) // transform
    .orElse("Upgrade to Pro!");     // fallback

System.out.println(result); // Upgrade to Pro!

Optional palvelukerroksessa

Realistinen palvelukerroksen esimerkki, jossa noudatetaan parhaita käytäntöjä.

import java.util.Optional;

class OrderService {
    public Optional<OrderSummary> getOrderSummary(long orderId) {
        return orderRepo.findById(orderId)
            .filter(o -> !o.isCancelled())
            .map(this::toSummary);
    }

    public OrderSummary requireOrderSummary(long orderId) {
        return getOrderSummary(orderId)
            .orElseThrow(() -> new OrderNotFoundException(orderId));
    }

    private OrderSummary toSummary(Order o) { return new OrderSummary(o); }
}
// API callers choose: Optional (handle absence) or require (exception)

Optional ja null-turvallisuuden työkalut

Optional täydentää @Nullable- ja @NonNull-annotaatioita sekä SpotBugsin ja NullAwayn kaltaisia työkaluja ja mahdollistaa kattavan null-turvallisuuden.

// Annotation-based: declares intent for in-class nulls
import org.jetbrains.annotations.*;

class ProductService {
    @Nullable  // may return null — for framework/bean use
    public String getCachedName(String id) { return cache.get(id); }

    // Optional — for service API that handles absence
    public Optional<Product> findProduct(String id) {
        return repo.findById(id);
    }
}
// Use @Nullable for fields/parameters; Optional for return types

Pikakysymys

Mikä seuraavassa koodissa on vialla?

if (opt.isPresent()) {
    process(opt.get());
}

Kertaus: Optional-tyypin parhaat käytännöt

Keskeiset opit:

  • Käyttäkää Optional-tyyppiä vain palautustyyppinä — älkää koskaan kenttänä tai metodin parametrina
  • Suosikaa orElseGet-metodia orElse-metodin sijaan, kun oletusarvon laskeminen on kallista
  • Käyttäkää orElseThrow-metodia, kun puuttuva arvo merkitsee ohjelmointivirhettä
  • Välttäkää isPresent() + get() -rakennetta — käyttäkää sen sijaan ifPresent-, map- tai orElse-metodia
  • Älkää koskaan sijoittako Optional-olioita kokoelmiin — suodattakaa streamit sen sijaan
  • Rakentakaa luettavia ketjuja: filter → map → orElse selkeää tietovirtaa varten
Aloita maksutta

Opi Java tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
104
Oppitunnit
374

Usein kysytyt kysymykset

Onko oppitunti ”Optionalin parhaat käytännöt” ilmainen?

Kyllä – oppitunnin ”Optionalin parhaat käytännöt” koko tekstin voi lukea täällä verkossa ilmaiseksi. Jos haluat harjoitella interaktiivisesti sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla sekä avata koko Java Academy-kurssin, päivitä CoddyKit PROhon. Java Academy-kurssilla on yhteensä 4 oppituntia.

Mitä opin oppitunnilla ”Optionalin parhaat käytännöt”?

Sovella metodeja orElse, orElseGet ja orElseThrow sekä vältä yleisiä Optionalin vastamalleja. Harjoittelet Java Academy-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni Java Academy-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin Java Academy-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 4/4.

Kuinka kauan ”Optionalin parhaat käytännöt”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä Java Academy-oppitunnilla?

Kyllä. Jokainen Java Academy-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. Miksi Optional on olemassa
  2. Optional-olioiden luominen ja tarkastelu
  3. Optional-olioiden muuntaminen: map ja flatMap
  4. Optionalin parhaat käytännöt
← Takaisin: Java Academy