Java Optional Sınıfı Kullanımı
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:
isPresent
: Optional nesnesi doluysatrue
, boşsafalse
dönerisEmpty
: Optional nesnesi boşsatrue
, doluysafalse
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:
Optional
boşsa birNoSuchElementException
oluşturan veyaOptional
doluysa sarılmış değeri döndüren, argüman almayan formThrowable
nesneler oluşturan veOptional
boşsa oThrowable
nesneyi atan veyaOptional
doluysa sarılmış değeri döndüren birSupplier
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.
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 GoetzOptional.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 GoetzOptional
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:
- Bir değerin mevcut olması veya olmaması bekleniyorsa,
- Bir değerin olmaması bir hata olarak değerlendirilmediğinde,
- 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:
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) {
// ...
}
}