I am refactoring the code to Java 8 and I want to replace null checks with Optional.
public Employee findEmployeeById(String id) {
List<Employee> empList = .. //some db query
return (empList.isEmpty() ? null : empList.get(0));
}
Optional.ofNullable(empList.get(0)) won't work as when it will throw IndexOutofBoundException
Or should I ideally replace null with Optional.empty()?
As #Jesper already mentioned in the comments, you have to check whether the list is empty and then return an empty Optional.
public Optional<Employee> findEmployeeById(String id) {
List<Employee> empList = .. //some db query
return empList.isEmpty() ? Optional.empty() : Optional.of(empList.get(0));
}
An Optional is a wrapper around a potentially null value that allows you to avoid explicitly checking for null when you use it.
Have a look at the Optional documentation to see what functionality it provides.
For example you can get an employee's name or "unknown" if it's absent, without checking for null:
Optional<Employee> emp = findEmployeeById(id);
String name = emp.map(Employee::getName).orElse("unknown");
You may read this post about Uses for Optional to see if it makes sense for you to use Optional.
To me the natural solution would be to keep your ? : construct as in Floern’s answer. If, however, you want to get rid of that, there is also an elegant solution without it:
public Optional<Employee> findEmployeeById(String id) {
List<Employee> empList = .. //some db query
return empList.stream().findFirst();
}
This gives you what you want because findFirst() returns an Optional. If you don’t care which element you get — or you know there’s never more than one — you may alternatively use findAny(), it too returns an Optional.
Why don't you simply replace your method with:
public Optional<Employee> findEmployeeById(String id) {
List<Employee> empList = .. //some db query
return (empList.isEmpty() ? Optional.empty() :
Optional.ofNullable(empList.get(0)));
}
I suggest you wrap the empList.get(0) in a Optional.ofNullable in case it might still be null.
As far as why that is better: think about the caller of the method. Whomever is now calling your method has to think what to actually do when the result is empty.
Besides you are now forced into writing code like:
Optional<Employee> emp = findEmployeeById("12");
if (emp.isPresent()) {
} else {
....
}
You can also chain this to become more fluent like:
emp.orElseThrow(RuntimeException::new)
Or other Optional methods.
That is simply not the case when you return an Employee. You are not even thinking (usually) to check if the reference is null.
That makes your code less error-prone and easier to understand.
Another possibility would be to do it as follows:
return Optional.of(empList).filter(list -> !list.isEmpty()).map(list -> list.get(0));
This will automatically return an empty Optional in case the list is empty or empList.get(0) returns null.
If empList can be null, consider using Optional.ofNullable(empList) instead of Optional(empList).
Related
Optional<CustomerEntity> findById(Integer id) {
return Optional.ofNullable(em.find(CustomerEntity.class, Long.valueOf(id)));
}
public List<Booking> getBooking(Integer id) {
return customerRepository.findById(id).get().getBooking();
}
The problem is findById might return null, so I want something like
customerRepository.findById(id).ifPresent().getBooking();
I could do it by manually check the ifpresent() return value, but I want to make it one line if possible.
You can use a combination of Optional.map and Optional.orElse to get the behavior you want. But I'd recommend returning an empty list instead of null, which could save you some headache at some point down the line:
return customerRepository
.findById(id)
.map(CustomerEntity::getBooking)
.orElse(List.of());
By returning List.of() in the default case, you can also be sure that the returned empty list cannot be altered by adding elements afterwards.
so I want to return Set from a entity in database. So I have crudRepository and first I return optional Knight from repo looking by id and then I want to get his set of quests from Optionlal
public Set<Quest> getAllUserStories(Long id)
{
Optional<Knight> knight = knightRepository.findById(id);
Set<Quest>set = knight.stream().map(k->k.getQuests()).collect(Collectors.toSet());
return listToReturn;
}
it almost works but its set of sets and I dont know how can I collet something without making it set?
Other question is what if that set of quests is empty, would collect work on null?
Your statement “but its set of sets” suggests that the method getQuests() returns a Set<Quest> already.
To flatten nested structures with a Stream, you’d use flatMap:
public Set<Quest> getAllUserStories(Long id) {
Optional<Knight> knight = knightRepository.findById(id);
Set<Quest> set = knight.stream()
.flatMap(k -> k.getQuests().stream())
.collect(Collectors.toSet());
return set;
}
But since there is at most one Knight and hence one Set<Quest> here, there is no need to go the Stream route at all. You can simply use
public Set<Quest> getAllUserStories(Long id) {
Optional<Knight> knight = knightRepository.findById(id);
Set<Quest> set = knight.map(Knight::getQuests).orElse(Set.of());
return set;
}
following the general pattern for arbitrary properties, in other words, the fact that this property has a Set type doesn’t matter. All you need, is a suitable default value for the empty optional. Here, Set.of() provides an immutable empty set. It’s similar to Collections.emptySet(), except that it has stricter null handling, i.e. does even reject query attempts like contains(null).
If you’re suspecting that there might be code having problems with such a strict null handling, you may use Collections.emptySet() instead.
public Set<Quest> getAllUserStories(Long id){
//Assuming Knight is a List or Set
Optional<Knight> knightOptional = knightRepository.findById(id);
//check here if value is present in the optional or not
Knight knight = knightOptional.isPresent() ? knightOptional.get() : null;
if(knight != null){
return knight.stream().map(k->k.getQuests()).collect(Collectors.toSet());
}
return new HashSet<Quest>();
}
I want to create method, which will use Optional functionality and return value NodeId.
This value I should extract from Asset object.
In some case I already use some functionality like ifPresent, filter, flatMap. But now I want clearly understand whether can I use Optional with simple methods like in example below, where I need just extract value from another Object
First example supposedly not very nice but however I try to Use Optional:
public Optional<NodeId> findParentNodeIdByAsset(Asset asset) {
Optional<Asset> tmpAsset = Optional.ofNullable(asset);
if(tmpAsset.isEmpty()) {
throw new NullPointerException();
}
return Optional.ofNullable(tmpAsset.get().getParents().iterator().next());
}
In second example I try to write same things but without Optional:
public NodeId tmpFindParentNodeIdByAsset(Asset asset) {
if(Objects.isNull(asset)) {
throw new NullPointerException();
}
return asset.getParents().iterator().next();
}
There's no point to check for null asset or empty tmpAsset if you're going to throw NullPointerException in these cases.
Just write:
public NodeId tmpFindParentNodeIdByAsset(Asset asset) {
return asset.getParents().iterator().next();
}
and the NullPointerException will be thrown if you try to de-reference a null reference.
Now, using an Optional becomes useful if you don't want to throw NullPointerException or if asset is not the only reference that may be null.
For example, suppose that asset.getParents() can also be null, and in case either asset or asset.getParents() are null, you want to return some default value, or an empty Optional.
You can chain multiple map() calls to transform each potentially null reference to the next potentially null reference, and end with either an Optional (as in the example below), a default value or an exception.
public Optional<NodeId> findParentNodeIdByAsset(Asset asset) {
return Optional.ofNullable(asset)
.map(asset -> asset.getParents())
.map(parents -> parents.iterator().next());
}
In addition, it might be safer to check that parents is not empty before trying to obtain the first element of its Iterator.
You aren't exactly using Optional correctly in your first method, it doesn't make much sense to throw a NullPointerException in the method where you're returning Optional. See Eran's answer for proper use.
If you, however, do want to throw a NullPointerException when the input is null, then would use this instead:
public NodeId tmpFindParentNodeIdByAsset(Asset asset) {
Objects.requireNonNull(asset, "asset");
return asset.getParents().iterator().next();
}
By using Optional you ensure the caller being aware of the returned value might be null.
Using Optional makes the code more fluent and improves its readability.
In your case i would indeed use a Optional. For example:
public NodeId tmpFindParentNodeIdByAsset(Asset asset) {
return Optional.ofNullable(asset)
.map(asset -> asset.getParents().iterator().next())
.orElseThrow(UnsupportedOperationException::new)
}
Otherwise if you'd like to return the optional, just remove the orElseThrow(). For each method of getParents(), iterator() or next(), which could possibly return null, you should create a map chain not to fall into a NPE. For example:
public Optional<NodeId> tmpFindParentNodeIdByAsset(Asset asset) {
return Optional.ofNullable(asset)
.map(asset -> asset.getParents())
.map(parents -> parents.iterator().next());
}
I am not sure how to do this
class Department {
String deptName;
List<Person> employees;
}
class Person {
String personName;
}
The problem statement is to fetch the first name of the person working in a particular department.
This department can be optional. So this is how my method looks -
String getFirstPerson(Optional<Department> department, String defaultName) {
// TODO:
}
I know the traditional way of doing this but would like to see some Java 8 + lambda way to simplify this.
Still a newbie here - so please pardon if I am not using the correct format.
I also have a default name to use in case we dont find that value.
P.S. I know it is not best practice to send Optional as method parameter. This is not the actual code. I am just trying to simplify it.
You can use the map function on Optional to get the employees list and then use stream get the first name or return defaultName. Even incase if Optional is empty you will get the defaultName
String getFirstPerson(Optional<Department> department, String defaultName) {
return department.map(d->d.getEmployees().stream().map(Person::getPersonName).findFirst().orElse(defaultName)).orElse(defaultName));
}
If you have a chance of getting null on getEmployees you can use below approach
department.map(Department::getEmployees)
.filter(Objects::nonNull)
.map(emp->emp.stream().map(Person::getPersonName).findFirst().orElse(defaultName)).orElse(defaultName)
A simplified way of doing that could be using emptyList for absent department or nullable employees:
String getFirstPerson(Optional<Department> department, String defaultName) {
return department.map(Department::getEmployees)
.orElse(Collections.emptyList()) // get rid of this ensuring non null List
.stream()
.map(Person::getPersonName)
.findFirst()
.orElse(defaultName);
}
Actually in:
department.map(Department::getEmployees)
.filter(Objects::nonNull)
.map(emp->emp.stream().map(Person::getPersonName).findFirst().orElse(defaultName)).orElse(defaultName)
we don't have to check non null since .map(Department::getEmployees) will return Optional.empty() if employees are null. See Optional documentation.
Correct answer would be without redundant filter:
department.map(Department::getEmployees)
.map(emp->emp.stream().map(Person::getPersonName).findFirst().orElse(defaultName)).orElse(defaultName)
Or alternatively:
department.map(Department::getEmployees)
.map(List::stream)
.map(Stream::findFirst)
.flatMap(Functions.identity())
.map(Person::getPersonName)
.orElse(defaultName);
In the following code, the ssNo parameter should ensure that only a single Employee satisfies the query condition:
Employee employee = null;
List<Employee> results = (List<Employee>) query.execute(ssNo);
if (results.iterator().hasNext())
{
for (Employee r : results)
{
employee = r;
}
}
return employee;
But if there are multiple results, the for loop ensures that the last Employee in the loop will be returned:
for (Employee r : results)
{
employee = r;
}
Is there a cleaner way to perform this kind of check?
Following the mantra that "less code is good", this code is equivalent to your code, but expressed in much less code and more clearly.
List<Employee> results = (List<Employee>) query.execute(ssNo);
return results.isEmpty() ? null : results.get(results.size() - 1);
It's more common to see the first element returned:
return results.isEmpty() ? null : results.get(0);
Another common pattern is:
if (results.size() > 1)
throw new IllegalStateException("Multiple results found, but at most one was expected");
Note that you can over-abbreviate your code to the point of "encryption", but as long as the code is still clear, less code is always better than more code.
Choosing the last employee is not a good idea, because if you expect to get only one employee, but instead, you get several ones, then you are likely to have a bug in your application or some data integrity problem that will go unnoticed, because you just return an arbitrary one. I'd throw an exception instead.
A clean API would look something like this:
// This returns a list of employees matching your search criteria
// Typical criteria are names, age, salary ranges, etc
// It will never be null, but maybe an empty list
List<Employee> getEmployeesByCriteria(... criteria);
// This will return at most one employee, depending on your search criteria
// Typically, you'll use an ID as criteria. If you don't find the employee
// you can either return null, or throw an exception. If you find several
// employees, then you should always throw an exception.
Employee getEmployeeByCriteria(... criteria) throws SomeException;
A minor simplification could be to remove the if check since the loop will get the last result anyway if the results are more than one.
If the result is only one that the first one is the last one so the if check is not needed.
I have to agree with Lukas Eder. If you're using a database, you should make sure the ssNo field is unique. If you are working only in memory, you should use a Hashtable using the ssNo as a key, and that would throw an exception when you try to insert on a key already used.
In any case, this part of your code is not the place (or this should not be) to check the validity of your datas.
If the query return more the one expected results you should throw an exception that this is not correct.
But if you are 100% sure that this is good way for your case then this look better.
List<Employee> results = (List<Employee>) query.execute(ssNo);
if(results.size() > 0) {
return results.get(results.size() - 1);
}