Java Optional Sınıfı Kullanımı
13 min read

Java Optional Sınıfı Kullanımı

Java Optional sınıfı, null değerleri başarılı ve etkili bir şekilde handle ederek NullPointerExceptions'a karşı koruma sağlar.
Java Optional Sınıfı Kullanımı
Photo by Mohammad Rahmani / Unsplash

Java Optional sınıfı, null değerlerle zarif bir şekilde ilgilenen bir kapsayıcıdır. Optional sınıfı, uygulamanızı çalışma zamanında NullPointerExceptions'a karşı koruma sağlamak için birden çok null değer denetime olan ihtiyacı ortadan kaldırmak için Java 8 ile birlikte Java.util paketine eklenmiştir.

Optional sınıfından önce, birçok geliştirici, istenen bir değerin bulunmadığını belirtmek için null veya exception kullanırdı. Artık Optional sınıfını kullanarak, bir değerin mevcut olup olmadığını rahatlıkla kontrol edebilirsiniz.

1. Neden Optional Kullanmalıyız?

Java, çoğu Nesne Yönelimli (OOP) dilde olduğu gibi, tip kullanımında oldukça dikkat ve kontrol gerektirir. Örneğin, aşağıdaki metoda sahip olduğumuzu varsayalım:

public User getUser();

Bu yöntemin User tipinde bir nesne döndürdüğü açıktır, ancak başka bir değer de döndürebilir: null. Herhangi bir primitive olmayan tip null olarak ayarlanabildiğinden, bir geliştiricinin aşağıdaki yöntem implementasyonunu yazmasını engelleyen hiçbir şey yoktur:

public User getUser() {
	return null;
}

Bu durum, bu yöntemi çağıran istemci için zahmetli bir durum yaratır. getUser yönteminden döndürülen User nesnesini kullanmadan önce, bir istemci nesnenin null olup olmadığını kontrol etmelidir:

User user = getUser();

if (user == null) {
    // handle null case...
} else {
    // do something with the user object...
}

Bu yaklaşım, User nesnesinin kullanımının NullPointerException (NPE) ile sonuçlanmamasını sağlar. Fakat bu da başka bir sorun yaratır: Primitive olmayan herhangi bir nesne örtük olarak null ayarlanabilir. Bu nedenle, bir yöntemden döndürülen her nesneyi kullanmadan önce geçersizlik açısından kapsamlı bir şekilde kontrol etmeliyiz. Bunun yalnızca büyük bir uygulamada mümkün olmayacağını söylemeye gerek yok, aynı zamanda kodumuzun temizliğini de bozacaktır. Örneğin, null denetimler için fazladan satırlar eklemek, uygulamamız genelinde, User nesnesinin kullanımını daha az net hale getiren (if-else deyimi içinde gizlenmiş) boilerplate kod problemini ortaya çıkarır.

Temel sorun, bir yöntemin ne zaman null döndürmeyi amaçladığını bilmememizdir. Bundan emin olmadığımız için de, döndürülen herhangi bir nesnenin null olabileceğini varsaymak zorunda kalırız.

Yaygın olarak kullanılan bir çözüm, JavaDocs kullanılarak bir return değerinin null olabileceğini belgelemektir. Bu, orijinal soruna göre bir gelişme olsa da, yine de, bir istemcinin nesnenin kullanımından önce geçersizliği kontrol etmesini sağlamaz (yani, Java derleyicisi, javaDocs ta yazsanız bile bu null kontroller olmadan kodu derleyecektir). Benzer şekilde, @NotNull gibi anotasyonlar mevcuttur, ancak bunlar JavaDocs yaklaşımıyla aynı dezavantaja sahiptir.

Öyleyse, bir geliştiricinin, bir yöntemin dönüş değerinin mevcut olup olmayabileceğini açıkça belirtmesine izin veren bir mekanizmaya ihtiyacımız var. Bu mekanizmanın adı da Optional'dır.

2. Optional Nasıl Kullanılır?

Optional sınıfın arkasındaki kavramları anlayarak, artık Optional nesnelerin pratikte nasıl kullanılacağına bakabiliriz. Optional sınıf, iki kategoriye ayrılabilen geniş bir method paketi içerir: Bunlar oluşturma yöntemleri ve ilklendirme yöntemleridir.

2.1. Oluşturma Yöntemleri

Optional oluşturma yöntemleri, ihtiyaçlarımıza uygun çeşitli Optional nesneler oluşturmamıza izin veren statik yöntemlerdir. Şu anda bu tür üç yöntem vardır:

2.1.1. of

of statik yöntemi, var olan bir nesneyi bir Optional ile sarmamıza izin verir. Mevcut nesne null değilse, dolu bir Optional döndürülür:

Optional<User> user = Optional.of(new User());  // dolu Optional

Eğer obje null ise NullPointerException fırlatır.

Optional<User> user = Optional.of(null);  // NPE

2.1.2. ofNullable

ofNullable statik yöntemi, null olmayan bir değer iletildiğinde (yani, dolu bir Optional üretildiğinde) of yöntemiyle aynıdır, ancak null geçirildiğinde boş bir Optional üretecektir (yani, bir NPE atılmayacaktır):

Optional<User> user1 = Optional.ofNullable(new User());  // dolu Optional
Optional<User> user2 = Optional.ofNullable(null);       // boş Optional

Bu yöntem, bir nesnenin geçersizliği bilinmediğinde yaygın olarak kullanılır.

2.1.3. empty

empty statik yöntem yalnızca boş bir Optional oluşturur:

Optional<User> user = Optional.empty();

Bu yöntem, tanım gereği, aşağıdakilerle aynıdır:

Optional<User> user = Optional.ofNullable(null);

Aşağıdaki bölümlerde göreceğimiz gibi, empty yöntemi genellikle hiçbir değerin olmadığı önceden bilindiğinde kullanılır.

2.2. İlklendirme Yöntemleri

İlklendirme yöntemleri, mevcut Optional nesnelerle etkileşime girmemize ve öncelikle bir Optional'ın durumunu sorgulamaya, bir Optional ile sarılmış nesneyi elde etmeye veOptional nesneleri manipüle etmeye odaklanmamıza izin verir.

2.2.1. isPresent ve isEmpty

Belirli bir Optional öğesinin boş olup olmadığını kontrol etmemize olanak tanıyan iki sorgu yöntemi mevcuttur:

  1. isPresent: Optional nesnesi doluysa true, boşsa false döner
  2. isEmpty: Optional nesnesi boşsa true, doluysa false döner

Bu nedenle, dolu bir Optional nesnesinin sorgu yöntemleri aşağıdakileri döndürür:

Optional<User> populated = // ...dolu Optional...

populated.isPresent();    // true
populated.isEmpty();      // false

Boş bir Optional nesnesinin sorgu yöntemleri aşağıdakileri döndürür:

Optional<User> empty = // ...boş Optional...

empty.isPresent();    // false
empty.isEmpty();      // true

2.2.2. get

get yöntemi, Optional nesnesi doluysa o Optional'ın sardığı değeri alır. Eğer Optional boşsa bir NoSuchElementException fırlatır. Bu yöntem, Optional'ın dolu olduğunu garanti edebildiğimizde, mevcut bir Optional'ı değerine dönüştürmek için kullanışlıdır, ancak bu yöntemi dikkatli kullanmalıyız.

Pratikte, bir Optional'ın dolu olduğunu garanti etmek için, ilk olarak isPresent veya isEmpty yöntemlerini kullanarak Optional'ı  sorgulamamızı ve ardından get'i çağırmamızı gerektirir:

Optional<User> possibleUser = getUser();

if (possibleUser.isPresent()) {
    User user = possibleUser.get();
    // ...use the user object...
} else {
    // ...handle case of missing User...
}

Bu modelle ilgili sorun,Optional'ı tanıtmadan önce gerçekleştirdiğimiz null denetime çok benzemesidir. Bu nedenle, bu yaklaşım, Optional sınıfın doğal faydalarını ortadan kaldırır. Çoğu durumda, dolu bir Optional ile ilişkili değeri elde etmek için get yöntemini kullanmaktan kaçınmalı ve orElse veya orElseThrow gibi diğer yöntemlerden birini kullanmalıyız.

2.2.2. orElse ve orElseGet

OrElse yöntemi, bir Optional nesnesi doluysa yöntemle sarılmış değeri elde etmemize olanak tanır. Eğer Optional boşsa, sarılan metodu ilklendirmemize izin verir. Örneğin, bir Optional nesnesi verildiğinde, orElse yöntemi bir User nesnesi alır. Optional boş değilse, dolu değeri döndürür; fakat boşsa, orElse yöntemine geçtiğimiz User nesnesini döndürür:

Optional<User> possibleUser = getUser();

User user = possibleUser
    		.orElse(new User());            

Bununla birlikte, varsayılan bir değer oluşturmanın pahalı bir işlem olabileceği ve kullanılma olasılığının düşük olabileceği zamanlar vardır. Örneğin, varsayılan değer, uzak bir sunucuyla bağlantı kurulmasını gerektirebilir veya bir veritabanından extend edilmiş olabilir veya büyük bir arama gerektirebilir. Ayrıca Optional öğesinin dolu olması muhtemel olan durumlarda bile varsayılan değeri oluşturmak zorunda kalıyoruz ve bu da ciddi bir performans etkisine yol açabiliyor.

Optional sınıf, lazy şekilde varsayılan nesne oluşturabilen bir Supplier alan orElseGet yöntemini de içerir. Bu, Optional sınıfın yalnızca gerektiğinde varsayılan bir nesne oluşturmasına izin verir (yani, varsayılan nesne yalnızca Optional boş olduğunda oluşturulur). Örneğin:

Optional<User> possibleUser = getUser();

User user = possibleUser
    		.orElseGet(() -> { /* ...lazily create a User object... */ });            

2.2.3. orElseThrow

Optional sınıfı, OrElse yöntemine benzer şekilde, Optional'ın değeri boşsa, almaya çalışırken bir exception atmamıza izin veren orElseThrow yöntemini sağlar. Ancak orElse yöntemlerinden farklı olarak, orElseThrow yönteminin iki biçimi vardır:

  1. Optional boşsa bir NoSuchElementException oluşturan veya Optional doluysa sarılmış değeri döndüren, argüman almayan form
  2. Throwable nesneler oluşturan ve Optional boşsa o Throwable nesneyi atan veya Optional doluysa sarılmış değeri döndüren bir Supplier alan form

Örneğin, bir Optional nesnesinden aşağıdaki gibi bir User nesnesi elde edebiliriz:

Optional<Foo> possibleFoo = doSomething();

Foo foo = possibleFoo
    		.orElseThrow();

Optional boşsa, bir NoSuchElementException oluşturulur. Optional doluysa, sarmaladığı değeri döndürür. Bu nedenle orElseThrow yöntemi, işlevselliğinde get yöntemiyle aynıdır, ancak adı, amacının ne olduğunu daha iyi açıklar. Bu nedenle, orElseThrow yöntemi, bir Optional boş olduğunda, önce dolu olup olmadığı kontrol edilmeden (yani isPresent veya isEmpty sorgu yöntemleri kullanılmadan) bir NoSuchElementException atması gereken yerlerde kullanılmalıdır.

get yöntemi, yalnızca önce Optional'ın dolu veya boş durumu kontrol edildiği yerlerde kullanılmalıdır. orElseThrow yönteminin JDK 9'da get yönteminin kullanımıyla ilgili karışıklığı azaltmanın bir yolu olarak sunulduğunu ve get yöntemine tercih edilmesi gerektiğini unutmayın.

💡
[Java 8'de] yaptığımız birkaç hatadan biri, Optional.get()'in adlandırılmasıydı, çünkü isim insanları isPresent()'i çağırmadan onu aramaya davet ediyor, bu da ilk başta Optional'ın kullanmanın tüm amacını baltalıyor. - Brian Goetz
💡
Java 9'da, Optional.get()'i kullanımdan kaldırmayı önerdik, ancak buna halkın tepkisi ... soğuktu diyelim. Daha küçük bir adım olarak, orElseThrow()'u [Java] 10...'da get()'in mevcut zararlı davranışı için daha şeffaf bir şekilde adlandırılmış eşanlamlı olarak tanıttık. - Brian Goetz

Optional sınıf ayrıca,Optional boş olduğunda özel bir özel durum oluşturan override edilmiş bir orElseThrow yöntemi içerir. Bu yöntem, herhangi bir Throwable nesneyi (veya Throwable'ın bir alt sınıfı olan nesneyi) oluşturan bir Supplier'ı kabul eder ve onu atar. Örneğin:

Optional<User> possibleUser = getUser();

User user = possibleUser
    		.orElseThrow(() -> { /* ...lazily create a User object... */ });            

Bu, bir istemci eksik bir nesneyi bir hata olarak gördüğünde ve boş bir Optional'a erişirken bir exception atmak istediğinde kullanışlıdır. Yapıcının işlevsel biçimini kullanarak basit exception'lar atmak da yaygın bir uygulamadır:

Optional<User> possibleUser = getUser();

User user = possibleUser
    		.orElseThrow(SomeException::new);            

2.2.4. ifPresent ve ifPresentOrElse

ifPresent yöntemi, Optional doluysa, sarılmış değeri kullanarak bir eylem gerçekleştiren bir Consumer alır. Bu, öncelikle değer olmadığında herhangi bir eylem gerçekleştirmek istemediğimizde, orElse veya orElseThrow yöntemlerini kullanarak sarılmış nesneyi elde etmeye işlevsel bir alternatiftir. Örneğin:

Optional<User> possibleUser = getUser();

possibleUser.ifPresent(user -> { /* ...do something with user... */ });

Optional sınıf ayrıca, Optional boş olduğunda durumu ele almamıza izin veren benzer bir yöntem olan ifPresentOrElse içerir. ifPresentOrElse yöntemi tarafından kabul edilen ilk bağımsız değişken, Optional doluysa sarılmış değeri kullanarak bir eylem gerçekleştiren bir Consumer; ikinci bağımsız değişken, Optional boşsa bir eylem gerçekleştiren bir Runnable'dır. Bu nedenle, Consumer yalnızca Optional doluysa çağrılırken, Runnable yalnızca Optional boşsa çağrılır. Örneğin:

Optional<User> possibleUser = getUser();

possibleUser.ifPresentOrElse(
    user -> { /* ...do something with user... */ },
    () -> { /* ...do something when no user found... */ }
);

Bu yöntemlerin her ikisinin de yararı, Optional boşsa Consumer'ın hiçbir zaman çağrılmamasıdır. Benzer şekilde, ifPresentOrElse durumunda, Optional doluysa Runnable hiçbir zaman çağrılmaz. Bu, Optional'ın durumuna bağlı olarak lazy şekilde çağrılacak karmaşık veya pahalı işlemleri sağlamamıza olanak tanır.

Bu yöntemin sadece pahalı işlemler için kullanılmaması gerektiğini unutmayın. Bu yöntem, dolu bir değer üzerinde bir işlem gerçekleştirilmesi gerektiğinde kullanılmalıdır. Örneğin, bir nesneyi yalnızca varsa güncellemek istersek, aşağıdakine benzer bir şey yapabiliriz:

public class Bar {
    
    private boolean isUpdated = false;

    public void update() {
    	isUpdated = true;
    }
}

public Optional<Bar> findBar() {
    // ...return a populated Bar if it could be found...
}

findBar().ifPresent(bar -> bar.update());

Bu durumda, Bar nesnesi bulunmazsa herhangi bir işlem yapmakla ilgilenmiyoruz. Öyle olsaydık, bunun yerine ifPresentOrElse yöntemini kullanabilirdik.

2.2.5. map

map yöntemi, eğer Optional boş değilse, sarılmış bir değeri bir nesneden diğerine dönüştürmemize izin verir. Bu yöntem, sarılmış değerin pipeline boyunca geçirildiği ve yeni bir değere dönüştürüldüğü bir yöntem olarak düşünülebilir. Bu yöntem, eşlenen değeri üretmek için sarılmış değere uygulanan bir Function nesnesini kabul ederek çalışır. Optional boşsa, Function nesnesi hiçbir zaman çağrılmaz ve map yönteminden boş bir Optional döndürülür.

Bu yöntem, bir değerin mevcut olup olmadığını bilmediğimizde kullanışlıdır, ancak varsa, başka bir nesneye dönüştürülmesi gerekir. Bu, genellikle Veri Aktarım Nesnelerini (DTO'lar) depolayan bir veritabanından okurken yaygın bir kullanım durumudur. Çoğu uygulamada, DTO, bir domain nesnesini bir veritabanında verimli bir şekilde depolamak için kullanılır, ancak uygulamanın daha yüksek seviyelerinde, domain nesnesinin kendisine ihtiyaç duyulur. Bu nedenle, DTO'dan domain nesnesine dönüştürmemiz gerekir.

Bir veritabanı nesnesi araması yaparsak, nesneyi bulabilir veya bulamayabiliriz. Bu nedenle, bu, DTO'yu Optional olarak sarmak için iyi bir kullanım durumudur. Domain nesnesine dönüştürmek için map yöntemini kullanabiliriz. Örneğin, bir Person nesnesinin adını tek bir satırda saklayan bir DTO'muz (PersonDto) olduğunu ve Person nesnesinin adının ad ve soyad olarak ayrıldığını varsayalım (yani, PersonDto nesnesinde ad, "Ismet BALAT" olarak depolanır. Ancak Person nesnesinde "Ismet" ilk adıyla ve "BALAT" soyadıyla depolanır). PersonDto'dan Person nesnesine dönüştürmek için bir mapper nesnesi kullanabilir ve bir veritabanından döndürülen PersonDto nesnesini bir Person nesnesine eşleyebiliriz:

public class Person {

    private String firstName;
    private String lastName;

    // ...getters & setters...
}

public class PersonDto {

    private String name;

    // ...getters & setters...
}

public class PersonMapper {

    public Person fromDto(PersonDto dto) {

        String[] names = dto.getName().split("\\s+");

        Person person = new Person();
        person.setFirstName(names[0]);
        person.setLastName(names[1]);

        return person;
    }
}

public class Database {

    public Optional<PersonDto> findPerson() {  
        // ...return populated DTO if DTO is found...
    }
}

Database db = new Database();
PersonMapper mapper = new PersonMapper();

Optional<Person> person = db.findPerson()
    							.map(mapper::fromDto);

Boş bir Optional ile sonuçlanması gereken bir dönüşümün mümkün olduğunu unutmayın. Örneğin, belirli bir nesneden diğerine dönüştürme mümkün değilse, map yöntemi boş bir Optional döndürmelidir. Bu tekniği gerçekleştirmek için bir anti-pattern, Function nesnesinin null döndürmesini sağlamaktır. Bu nesne daha sonra map yöntemiyle (null nesnemizin bir exception atmadan sarılmasına izin veren ofNullable kullanılarak) boş bir Optional olarak sarılır:

Optional<Person> person = db.findPerson()
	.map(dto -> {
        if (dtoCanBeConverted()) {
            return mapper.fromDto(dto);
        } else {
            return null;
        }
 	});

dtoCanBeConverted yöntemi false olarak değerlendirilirse, Function nesnesi null değerini döndürür ve bu da person'ın boş bir Optional olmasıyla sonuçlanır. Bu yaklaşım kusurludur, çünkü Optional sınıfın asıl amacı olan null değerlerin kullanımını yeniden sunar. Bunun yerine, flatMap yöntemini kullanmalı ve açıkça boş bir Optional döndürmeliyiz.

2.2.6. flatMap

flatMap yöntemi, flatMap'in sarılmış değeri yeni bir Optional'a dönüştüren bir Function nesnesini kabul etmesi dışında, map yöntemine benzer. map yönteminden farklı olarak, flatMap, tercih ettiğimiz bir Optional döndürmemize izin verir. Bu nedenle, mapper sarılmış değeri dönüştüremiyorsa, açıkça boş bir Optional olarak döndürebiliriz:


Optional<Person> person = db.findPerson()
    .flatMap(dto -> {
        if (dtoCanBeConverted()) {
            Person person = return dao.fromDto(dto);
            return Optional.ofNullable(person);
        } else {
            return Optional.empty();
        }
    });    

map yöntemiyle yaptığımız gibi artık bir Person nesnesini basitçe döndüremeyeceğimizi belirtmek önemlidir. Bunun yerine, dönüştürülen nesneyi bir Optional olarak sarmaktan artık sorumluyuz. Function nesnesi bir null Optional döndürürse, bir NPE atıldığını unutmayın. Örneğin, aşağıdakiler yürütüldüğünde bir NPE atar:

Optional<Person> person = db.findPerson()
    							.flatMap(dto -> null);                            

2.2.7. filter

filter yöntemi, dolu bir Optional olarak sağlanan Predicate'ı karşılıyorsa, dolu bir Optional döndürmemizi sağlar. Bu nedenle, filter yöntemi boş bir Optional olarak uygulanırsa, Predicate çağrılmayacaktır. Benzer şekilde, filter yöntemi dolu bir Optional'a uygulanırsa ancak sarılmış değer sağlanan Predicate'ı karşılamıyorsa (yani, Predicate nesnesinin test yöntemi false olarak değerlendiriliyorsa), boş bir Optional döndürülür. Örneğin:

public class Bar {

    private int number;
    
    public Bar(int number) {
        this.number = number;
    }

    // ...getters & setters...
}

Optional.of(new Bar(1))
    .filter(bar -> bar.getNumber() > 0)
    .isPresent();              // true

Optional.of(new Bar(-1))
    .filter(bar -> bar.getNumber() > 0)
    .isPresent();              // false

3. Optional Ne Zaman Kullanılmalıdır?

Optional sınıfın en tartışmalı yönlerinden biri, ne zaman kullanılması ve kullanılmaması gerektiğidir. Bu bölümde, Optional'ların uygun olabileceği veya olmayabileceği yöntem dönüş değerleri (return values), alanlar (fields) ve parametreler (parameters) gibi bazı yaygın kullanım durumlarına bakacağız.

3.1 Dönüş Değerleri

Bu makale boyunca gördüğümüz gibi, Optional, amaçlanan amacı bu olduğundan, yöntemlerin dönüş değerleri için çok uygundur. Optional sınıf belgelerine göre:

💡
Optional, öncelikle "no result" ifadesinin açıkça gerekli olduğu ve null değerin kullanılmasının hatalara neden olabileceği durumlarda bir yöntem dönüş türü olarak kullanılmak üzere tasarlanmıştır.

Genel olarak, aşağıdaki durumlarda bir Optional dönüş değeri olarak kullanılmalıdır:

  1. Bir değerin mevcut olması veya olmaması bekleniyorsa,
  2. Bir değerin olmaması bir hata olarak değerlendirilmediğinde,
  3. Eksik bir değerin ele alınmasından client sorumluysa

Optional dönüş değerleri, genellikle istenen nesneyi bulabilen veya bulamayan sorgular için kullanılır. Örneğin, repository'ler genellikle aşağıdaki şekilde tanımlanır:

public interface BookRepository {
    public Optional<Book> findById(long id);
    public Optional<Book> findByTitle(String title);
    // ...
}

Bu, istemcinin, eksik bir nesneyi yok sayarak, varsayılan bir nesne oluşturarak veya bir özel durum oluşturarak, yöntemin çağrıldığı bağlama uygun bir tarzda eksik bir Book nesnesini işlemesine olanak tanır.

3.2 Field

Optional nesneler, dönüş türleri için çok uygun olsa da, field alanları için daha az uygundur. Aşağıdakine benzer bir field oluşturmak mümkündür, ancak bu kesinlikle tavsiye edilmez:

public class Bar {
    
    private Optional<Foo> foo;
    
    // ...getters & setters...
}

Optional sınıf serializable olmadığından (yani Serializable interface'i implement etmediğinden) Optional field'lardan kaçınılmalıdır. Brian Goetz'e göre:

💡
Elbette insan istediğini yapacaktır. Ancak bu özelliği eklerken net bir niyetimiz vardı ve bu, pek çok kişinin yapmamızı istediği kadar genel bir Belki türü olması değildi. Amacımız, "no result" ifadesinin açık bir yolunun olması gereken library yöntemi dönüş türleri için sınırlı bir mekanizma sağlamaktı ve bunun için null kullanmanın büyük olasılıkla hatalara neden olması muhtemeldi.

Bu nedenle, Optional türler yalnızca yöntem dönüş türleri için tasarlanmıştır. Fieldlar bir sınıfın iç durumunu oluşturduğundan ve dış istemciler tarafından görülmemesi gerektiğinden, bir field isteğe bağlı olarak kabul edilirse, bunun yerine bir Optional nesne döndüren bir getter oluşturulabilir:

public class Bar {

    private Foo foo;

    public Optional<Foo> getFoo() {
        return Optional.ofNullable(foo);
    }
}

Bu tekniği kullanarak, istemcilere, Bar'ın serializable özelliğini korunurken foo değerinin mevcut olabileceği veya olmayabileceği konusunda açıkça bilgi verilir.

3.3 Parametreler

Bir yöntemin veya yapıcının parametresinin Optional  olabileceği geçerli durumlar vardır, ancak bu amaç için bir Optional  kullanılmamalıdır. Örneğin, aşağıdakilerden kaçınılmalıdır:

public class Bar {

    public void doSomething(Optional<User> user) {
        // ...
    }
}

Parametre türünü Optional   olarak ayarlamak yerine yöntem override kullanılmalıdır:

public class Bar {

    public void doSomething() {
        // ...
    }
    
    public void doSomething(User user) {
        // ...
    }
}

Ayrıca, farklı yöntem adlarına sahip override edilmemiş yöntemler de kullanılabilir:

public class Bar {

    public void doSomething() {
        // ...
    }
    
    public void doSomethingWithUser(User user) {
        // ...
    }
}