Friends
I have below two overloaded methods which throw same ServiceException
private ModResponse updateNDelTerms(GlobalTermDeleteType item, boolean isGlobal)
{
StdTerm stdTerm = getTerm(item.getstdTermId());
if (stdTerm == null || !getBuilder().getOwnerId().equals(stdTerm.getOwnerId()))
{
throw new ServiceException(GLOBAL_TERM_TO_DELETE_DOES_NOT_EXIST_MSG,
CANNOT_DELETE_GLOBAL_TERM, GLOBAL_TERM_DOES_NOT_EXIST);
}
if (stdTerm.isGlobal() && !isGlobal)
{
throw new ServiceException(
"Global Term can not be updated: incorrect URL.",
CANNOT_UPDATE_GLOBAL_TERM, INCORRECT_URL);
}
if (stdTerm.isLocked() != null && stdTerm.isLocked())
{
throw new ServiceException("Global Term can not be updated: stdTerm is locked.",
CANNOT_UPDATE_GLOBAL_TERM, TERM_LOCKED);
}
return updateNDel(item, stdTerm);
}
Second method is
public ItemResponse<List<stdTermItemType>> copyTerm(
BigInteger stdTermId, boolean isGlobal,
boolean isFalse)
{
StdTerm stdTerm = getTerm(stdTermId);
if (stdTerm == null || !getBuilder().getOwnerId().equals(stdTerm.getOwnerId()))
{
throw new ServiceException(GLOBAL_TERM_TO_DELETE_DOES_NOT_EXIST_MSG,
CANNOT_DELETE_GLOBAL_TERM, GLOBAL_TERM_DOES_NOT_EXIST);
}
if (stdTerm.isGlobal() && !isGlobal)
{
throw new ServiceException(
"Global Term can not be updated: incorrect URL.",
CANNOT_COPY_GLOBAL_TERM, INCORRECT_URL);
}
if (stdTerm.isLocked() != null && stdTerm.isLocked())
{
throw new ServiceException("Rate sheet term can not be updated: stdTerm is locked.",
CANNOT_COPY_GLOBAL_TERM, TERM_LOCKED);
}
return copyGlobleTerm(stdTerm, pasteTermObj, isFalse);
}
I am thinking of refactoring these two methods and use Extract common code to a method ,
However due to difference in CANNOT_COPY_GLOBAL_TERM and CANNOT_UPDATE_GLOBAL_TERM which are unique to both the methods hence
Im not able to achieve Extract to a Method refactoring .
Please suggest .
private StdTerm retrieveStdTerm(BigInteger stdTermId, boolean isGlobal, String errorTerm)
{
StdTerm stdTerm = getTerm(BigInteger stdTermId);
if (stdTerm == null || !getBuilder().getOwnerId().equals(stdTerm.getOwnerId()))
{
throw new ServiceException(GLOBAL_TERM_TO_DELETE_DOES_NOT_EXIST_MSG,
CANNOT_DELETE_GLOBAL_TERM, GLOBAL_TERM_DOES_NOT_EXIST);
}
if (stdTerm.isGlobal() && !isGlobal)
{
throw new ServiceException(
"Global Term can not be updated: incorrect URL.",
errorTerm, INCORRECT_URL);
}
if (stdTerm.isLocked() != null && stdTerm.isLocked())
{
throw new ServiceException("Global Term can not be updated: stdTerm is locked.",
errorTerm, TERM_LOCKED);
}
return stdTerm;
}
private ModResponse updateNDelTerms(GlobalTermDeleteType item, boolean isGlobal)
{
StdTerm stdTerm = retrieveStdTerm(item.getstdTermId(), isGlobal,
CANNOT_DELETE_GLOBAL_TERM);
return updateNDel(item, stdTerm);
}
public ItemResponse<List<stdTermItemType>> copyTerm(BigInteger stdTermId, boolean isGlobal,
boolean isFalse)
{
StdTerm stdTerm = retrieveStdTerm(stdTermId, isGlobal, CANNOT_COPY_GLOBAL_TERM);
return copyGlobleTerm(stdTerm, pasteTermObj, isFalse);
}
Pass in the things that are different into the extracted method.
You could also turn those methods into lambdas and pass them in as well if you're running on JDK 8. Use a more functional style.
Related
env:
jdk: 17.0.1
mapstruct: 1.5.1.Final
Using the default configuration I generated the following code
protected AgentInfo wealthProdAccountInfoDTOToAgentInfo(WealthProdAccountInfoDTO wealthProdAccountInfoDTO) {
if ( wealthProdAccountInfoDTO == null ) {
return null;
}
String agentName = null;
String agentIdentityType = null;
String agentIdentityNo = null;
String agentIdentityExpireAt = null;
agentName = wealthProdAccountInfoDTO.getAgentName();
agentIdentityType = wealthProdAccountInfoDTO.getAgentIdentityType();
agentIdentityNo = wealthProdAccountInfoDTO.getAgentIdentityNo();
agentIdentityExpireAt = wealthProdAccountInfoDTO.getAgentIdentityExpireAt();
AgentInfo agentInfo = new AgentInfo( agentName, agentIdentityType, agentIdentityNo, agentIdentityExpireAt );
return agentInfo;
}
But I want to return null when all field of source are null, like this
protected AgentInfo wealthProdAccountInfoDTOToAgentInfo(WealthProdAccountInfoDTO wealthProdAccountInfoDTO) {
if ( wealthProdAccountInfoDTO == null ) {
return null;
}
// add check logic
if (agentName == null && agentIdentityType == null && agentIdentityNo == null && agentIdentityExpireAt == null) {
return null;
}
String agentName = null;
String agentIdentityType = null;
String agentIdentityNo = null;
String agentIdentityExpireAt = null;
agentName = wealthProdAccountInfoDTO.getAgentName();
agentIdentityType = wealthProdAccountInfoDTO.getAgentIdentityType();
agentIdentityNo = wealthProdAccountInfoDTO.getAgentIdentityNo();
agentIdentityExpireAt = wealthProdAccountInfoDTO.getAgentIdentityExpireAt();
AgentInfo agentInfo = new AgentInfo( agentName, agentIdentityType, agentIdentityNo, agentIdentityExpireAt );
return agentInfo;
}
how should I configure it?
Unfortunately there's no clean solution for your problem, except implementing code for null check by yourself, Marc specified the right approach to your problem (I'd go with it personally or would use default method for the same purpose).
I can add some workarounds, which will only work if mapping target is inner object:
Use #BeforeMapping to set input inner object to null, so when there will be null-check it will be skipped
#BeforeMapping
default void clearData(TestB source, #MappingTarget TestA target) {
TestD innerD = source.getInnerD();
if (innerD.getSecond() == null && innerD.getFirst() == null) {
source.setInnerD(null);
}
}
And it will generate the following code:
#Override
public TestA from(TestB input) {
....
clearData( input, testA ); //set input field to null
testA.setInnerC( fromInner( input.getInnerD() ) );
....
}
#Override
public TestC fromInner(TestD input) {
if ( input == null ) { //skip because of null
return null;
}
....
}
Use #AfterMapper to set output parameter to null(it will be mapped in the first place, so there will be some overhead)
#AfterMapping
default void clearData(TestB source, #MappingTarget TestA target) {
TestD innerD = source.getInnerD();
if (innerD.getSecond() == null && innerD.getFirst() == null) {
target.setInnerC(null);
}
}
And generated code will be:
#Override
public TestA from(TestB input) {
....
testA.setInnerC( fromInner( input.getInnerD() ) ); //field is actually mapped but cleared later
clearData( input, testA );
return testA;
}
As I said, these solutions aren't really clean and should be seen as workarounds only. Pros of these workaround is that you will keep working with autogenerated code and these hacks will be hidden inside that code.
UPD Stumbled upon #DecoratedWith lately and it also can do the trick. https://mapstruct.org/documentation/stable/reference/html/#_customizing_mappings
Just implement decorator for iterable2iterable mapping method: List<A> from(List<b> b) and just manually iterate over b checking if all b's fields are null and if so skip it
brute force... it's a simple class, so create a custom mapper
#Mapper
public interface AgentInfoMapper {
#Named("AgentInfoNullIfContentsNull")
public static AgentInfo custom(WealthProdAccountInfoDTO dto) {
if ( wealthProdAccountInfoDTO == null ) {
return null;
}
if (agentName == null && agentIdentityType == null && agentIdentityNo == null && agentIdentityExpireAt == null) {
return null;
}
// mapping code
}
}
https://www.baeldung.com/mapstruct-custom-mapper
Thanks to ArtemAgaev's idea, I ended up considering using #AfterMapping and java reflection for this type of scenario
#AfterMapping
default void cleanData(#MappingTarget AccountInfoDomain domain) {
Optional.ofNullable(domain).ifPresent(c -> {
if (isAllFieldNull(domain.getAgentInfo())) {
domain.setAgentInfo(null);
}
});
}
public static boolean isAllFieldNull(Object o) {
Object[] fieldsValue = getFieldsValue(o);
return Optional.ofNullable(fieldsValue).map(f -> Arrays.stream(f).allMatch(Objects::isNull)).orElse(true);
}
public static Object[] getFieldsValue(Object obj) {
if (null != obj) {
final Field[] fields = getFields(obj instanceof Class ? (Class<?>) obj : obj.getClass());
if (null != fields) {
final Object[] values = new Object[fields.length];
for (int i = 0; i < fields.length; i++) {
values[i] = getFieldValue(obj, fields[i]);
}
return values;
}
}
return null;
}
Sonar reported Confusing Ternary violation against the below code:
package com.example.dto;
import java.util.Date;
public class ShiftTemplateUserDTO {
private Date breakStartTime;
private Date breakEndTime;
public Date getBreakStartTime() {
return breakStartTime != null ? new Date(breakStartTime.getTime()) : null;
}
public void setBreakStartTime(Date breakStartTime) {
this.breakStartTime = breakStartTime != null ? new Date(breakStartTime.getTime()) : null;
}
public Date getBreakEndTime() {
return breakEndTime != null ? new Date(breakEndTime.getTime()) : null;
}
public void setBreakEndTime(Date breakEndTime) {
this.breakEndTime = breakEndTime != null ? new Date(breakEndTime.getTime()) : null;
}
}
I tried updating the code to add if/else condition but still Sonar is complaining about the same rule, what am I doing wrong?
public Date getBreakStartTime() {
if (breakStartTime != null) {
return new Date(breakStartTime.getTime());
} else {
return null;
}
}
public void setBreakStartTime(Date breakStartTime) {
if (breakStartTime != null) {
this.breakStartTime = new Date(breakStartTime.getTime());
} else {
this.breakStartTime = null;
}
}
Do not use negated conditions in ternary operators.
Try replacing this:
return breakStartTime != null ? new Date(breakStartTime.getTime()) : null;
with this:
return breakStartTime == null ? null : new Date(breakStartTime.getTime());
The same applies to your if-else logic.
Replace this:
if (breakStartTime != null) {
return new Date(breakStartTime.getTime());
} else {
return null;
}
with this:
if (breakStartTime == null) {
return null;
} else {
return new Date(breakStartTime.getTime());
}
Sonar doesn't like "un-equal" comparisons. It wants you to write foo == null ? null : ....
You also might consider that instead of doing this:
public Date getBreakStartTime() {
return breakStartTime != null ? new Date(breakStartTime.getTime()) : null;
}
You just do this:
public Date getBreakStartTime() {
return breakStartTime;
}
In your current code, if the Date object is not null, you are constructing a new Date object with the same time value as the original Date value. That results in effectively the same object. You might be concerned about immutability. Is that really a concern for you?
I have a pseudo Java method where everything can be nullable:
private String prepareMessage(Scope scope) {
if (scope != null) {
if (scope.getPermission != null) {
if (scope.getInfo != null) {
return "Successful";
} else {
return "Missing field Info";
}
} else if (scope.getInfo() != null) {
return "Permission field not provided";
}
}
return "Permission and Info fields not provided";
}
How can I simplify this code to remove terrible looking null checks? Thanks !
You can create a helper method that suits you, an example:
public String notNullOrElse(Object o, String notNull, String isNull) {
return null != o ? notNull : isNull;
}
Then you can change:
if (scope.getInfo != null) {
return "Successful";
} else {
return "Missing field Info";
}
to:
return notNullOrElse(scope.getInfo(), "Successfull", "Missing field Info");
However, sometimes those horrible null checks are unfortunately necessary.
Tried refactoring the code. In most cases you can simplify If - else by re ordering the code flow. This makes getting code coverage easy.
private String prepareMessage(Scope scope) {
if (scope == null) {
return "Permission and Info fields not provided";
}
if (scope.getPermission != null && scope.getInfo != null) {
return "Successful";
} else {
return scope.getPermission == null ? "Permission field not provided" : "Missing field Info";
}
}
This doc beautifully explains code smells in if-else ->
https://dzone.com/articles/code-smells-if-statements
Java 1.7 introduces the class java.util.Objects (this link is from the Java 11 JavaDoc, but that should not matter here).
That class provides the methods Objects.isNull() and Objects.nonNull() that can be used as follows in your code:
import static java.util.Objects.*;
…
private String prepareMessage(Scope scope) {
if (nonNull (scope)) {
if (nonNull (scope.getPermission())) {
if (nonNull (scope.getInfo())) {
return "Successful";
} else {
return "Missing field Info";
}
} else if (nonNull (scope.getInfo()) {
return "Permission field not provided";
}
}
return "Permission and Info fields not provided";
}
Whether this looks better or not is a matter of taste, of course.
Alternative logic may look like this (for Java 11):
import static java.util.Objects.*;
…
private String prepareMessage( Scope scope )
{
var messages = { "Successful", "Missing field Info", "Permission field not provided", "Permission and Info field not provided" };
var index = 0;
var retValue = messages [3]; // No scope at all
if( nonNull( scope ) )
{
if( isNull( scope.getPermission() ) ) index += 2;
if( isNull( scope.getInfo() ) ) index += 1;
retValue = messages [index];
}
return retValue;
}
I do not want to say that this is really better in any way, it is just different. And it spares one null check …
is there any other different solution for this code.
for every pojo class we have to check that modified data coming from browser and we will store only modified data into the database.
see below billingTax obj is coming from browser which is updated data
and billingtaxDbObject obj is retrieved from database and we will check with if condition whether updated data is changed or not
if pojo class has 20 fields, we have to write 20 if conditions
if pojo class has 5 fields, we have to write 5 if conditions
instead of writing if conditions for checking wheter data is modified or nor is there any other simplest way?
#Override
public BillingTax update(BillingTax billingTax) throws DataInsufficientException, RecordNotFoundException {
log.debug("BillingTaxServiceImpl.update()....................");
try {
if (billingTax == null)
throw new DataInsufficientException("billingTax object is null");
BillingTax billingtaxDbObject = get(billingTax.getId());
if (billingtaxDbObject == null)
throw new RecordNotFoundException("billingTax object is not found in database");
if (billingTax.getTaxApplyType() != null
&& !billingTax.getTaxApplyType().equals(billingtaxDbObject.getTaxApplyType()))
billingtaxDbObject.setTaxApplyType(billingTax.getTaxApplyType());
if (billingTax.getCode() != null && !billingTax.getCode().trim().equalsIgnoreCase("null")
&& !billingTax.getCode().equalsIgnoreCase(billingtaxDbObject.getCode()))
billingtaxDbObject.setCode(billingTax.getCode());
if (billingTax.getName() != null && !billingTax.getName().trim().equalsIgnoreCase("null")
&& !billingTax.getName().equalsIgnoreCase(billingtaxDbObject.getName()))
billingtaxDbObject.setName(billingTax.getName());
if (billingTax.getDescription() != null && !billingTax.getDescription().trim().equalsIgnoreCase("null")
&& !billingTax.getDescription().equalsIgnoreCase(billingtaxDbObject.getDescription()))
billingtaxDbObject.setDescription(billingTax.getDescription());
if (billingTax.getServiceTypeForTax() != null
&& !billingTax.getServiceTypeForTax().equals(billingtaxDbObject.getServiceTypeForTax()))
billingtaxDbObject.setServiceTypeForTax(billingTax.getServiceTypeForTax());
if (billingTax.getTaxValue() != null && !billingTax.getTaxValue().equals("null")
&& !billingTax.getTaxValue().equals(billingtaxDbObject.getTaxValue()))
billingtaxDbObject.setTaxValue(billingTax.getTaxValue());
if (billingTax.getStatus() != null && !billingTax.getStatus().equals(billingtaxDbObject.getStatus()))
billingtaxDbObject.setStatus(billingTax.getStatus());
if (billingTax.getOrderNo() != null && !billingTax.getOrderNo().equals("null")
&& !billingTax.getOrderNo().equals(billingtaxDbObject.getOrderNo()))
billingtaxDbObject.setOrderNo(billingTax.getOrderNo());
if (billingTax.getId() != null && !billingTax.getId().trim().equalsIgnoreCase(billingtaxDbObject.getId())
&& !billingTax.getId().equalsIgnoreCase(billingtaxDbObject.getId()))
billingtaxDbObject.setId(billingTax.getId());
if (billingTax.getStartDate()!= null && !billingTax.getStartDate().equals(billingtaxDbObject.getStartDate()))
billingtaxDbObject.setStartDate(billingTax.getStartDate());
if (billingTax.getEndDate()!= null && !billingTax.getEndDate().equals(billingtaxDbObject.getEndDate()))
billingtaxDbObject.setEndDate(billingTax.getEndDate());
billingtaxDbObject.setUpdatedDate(new Date());
return billingTaxDAO.update(billingtaxDbObject);
} catch (Exception e) {
log.error("BillingTaxServiceImpl.update()....................exception:" + e.getMessage());
throw e;
}
}
You can do it with Dynamic updates for hibernate if you can avoid check changes among dto and entity and update all fields that come from web. If you need check dto from web and entity you can use apache bean util to find all changed values (or use spring util if you have or reflection from java...) and update it with dynamic updates.
see : BeanUtils
BeanUtils.copyProperties() // there are 3 methods.
check how it works in source code .
Create your own util method , similar to BeanUtils.copyProperties() , but with logic that you need (not null and not equal with source-entity value ).
Also use method from BeanUtils , to get PropertyDescriptor :
public static PropertyDescriptor[] getPropertyDescriptors(Class clazz)
throws BeansException
iterate over array of PropertyDescriptor and do check that you need (set value into source with ReflectionUtils).
with this approach you populate only properties that are not null and changed( if you need it) into billingtaxDbObject and update it.
You can put your copy / merge method into some util class and reuse it for all place where you need copy from dto into entity with some checks.
this is developed by getting all the fields/properties from the pojo class and checking with wheter null data or modified data.
below is the code:
copyProperties(billingTax, billingtaxDbObject);
public void copyProperties(BillingTax source, BillingTax dest) throws Exception{
if (source == null)
throw new DataInsufficientException("billingTax object is null");
if (dest == null)
throw new RecordNotFoundException("billingtaxDbObject object is not found in database");
try{
for (Field field : source.getClass().getDeclaredFields()) {
field.setAccessible(true);
Object sourceValue = field.get(source);
Object destValue=field.get(dest);
if(sourceValue!=null && sourceValue!="" && sourceValue!="null"){
if(sourceValue instanceof String){
if(!sourceValue.toString().trim().equalsIgnoreCase("null")){
if(!sourceValue.toString().equalsIgnoreCase(destValue.toString())){
field.set(dest, sourceValue);
System.err.println(field.getName()+" is modified:"+field.get(dest));
}
}
}
else{
if(!sourceValue.equals("null") && !sourceValue.equals(destValue)){
field.set(dest, sourceValue);
System.err.println(field.getName()+" is modified:"+field.get(dest));
}
}
}
}
System.out.println();
}catch (Exception e) {
throw e;
}
}
public class CopyConverter<T> {
private List<String> errorMessages = new ArrayList<>();
private int countSuccess = 0;
public CopyConverter<T> convert(T source, T target, Set<String> ignoreFields){
copyProperties(source,target,ignoreFields);
return this;
}
public boolean hasError(){
return errorMessages.isEmpty();
}
public List<String> getErrorMessages(){
return errorMessages;
}
private boolean copyProperties(T source ,T target , Set<String> ignoreFields) {
Objects.requireNonNull(source , "..error message...");
Objects.requireNonNull(target , "..error message...");
try {
Map<String, Field> fieldNameMapping = buildFiledMap(target.getClass().
getDeclaredFields());
ignoreFields = (ignoreFields == null ? new HashSet<>() : ignoreFields);
for (Map.Entry<String, Field> fieldEntry : fieldNameMapping.entrySet()) {
if (ignoreFields.contains(fieldEntry.getKey())) {
continue;
}
Field field = fieldEntry.getValue();
field.setAccessible(true);
Object sourceValue = field.get(source);
Object targetValue = field.get(source);
if (isChangedAsString(sourceValue, targetValue)) {
field.set(target, sourceValue);
continue;
}
if (isChangedAsObject(sourceValue, targetValue)) {
field.set(target, sourceValue);
}
countSuccess++;
}
} catch (Exception e) {
errorMessages.add(".......");
log.info(....);
}
return countSuccess == 0;
}
private Map<String, Field> buildFiledMap(Field[] fields) {
Map<String, Field> fieldMap = new HashMap<>(fields.length);
//Stream.of(fields).collect(Collectors.toMap())
for (Field field : fields) {
fieldMap.put(field.getName(), field);
}
return fieldMap;
}
private boolean isChangedAsObject(Object obj1, Object obj2) {
return (obj1 == null && obj2 != null) || (obj1 != null && !obj1.equals(obj1));
}
private boolean isChangedAsString(Object obj1, Object obj2) {
if (obj1 instanceof String && obj2 instanceof String) {
String value1 = (String) obj1;
String value2 = (String) obj2;
return value1 != null &&
!value1.trim().equalsIgnoreCase("null") &&//strange check
!value1.equalsIgnoreCase(value2);
}
return false;
}
}
Instead of multiple null checks in below case, I am planning to add something readable code instead. May be with the help of java 8 streams/maps. Can somebody help me with this
private String getRailsServiceClass(IRailsComponent railsComponent) {
String serviceClass = "";
if (railsComponent != null && railsComponent.getRailOffer() != null && railsComponent.getRailOffer().getRailProducts().get(0).getRailProduct() != null && railsComponent.getRailOffer().getRailProducts().get(0).getRailProduct().getFareBreakdownList() != null &&
railsComponent.getRailOffer().getRailProducts().get(0).getRailProduct().getFareBreakdownList().get(0).getPassengerFareList() != null && railsComponent.getRailOffer().getRailProducts().get(0).getRailProduct().getFareBreakdownList().get(0).getPassengerFareList().get(0).getPassengerSegmentFareList() != null &&
railsComponent.getRailOffer().getRailProducts().get(0).getRailProduct().getFareBreakdownList().get(0).getPassengerFareList().get(0).getPassengerSegmentFareList().get(0).getCarrierServiceClassDisplayName() != null) {
return railsComponent.getRailOffer().getRailProducts().get(0).getRailProduct().getFareBreakdownList().get(0).getPassengerFareList().get(0).getPassengerSegmentFareList().get(0).getCarrierServiceClassDisplayName();
}
return serviceClass;
}
You can use Optional for your purpose.
String serviceClass = Optional.ofNullable(railsComponent)
.map(IRailsComponent::getRailOffer)
.map(RailOffer::getRailProducts)
...
.orElse("");
Your code is bad because on each check you had to get the items in the lists again and again. That's a lot of I/O to do.
Without using any API (so pre-Java 8 solution) You can clean you code by checking each item one after all (depends on the accessiblity of each class but here is a fully developped condition
RailComponent rc = getRailComponent();
if (rc != null) {
RailOffer ro = rc.getRailOffer()
if (ro != null) {
RailProduct rp = ro.getRailProducts().get(0).getRailProduct();
if (rp != null) {
List<FareBreakDown> fbList = rp.getFareBreakdownList();
if (fbList != null) {
List<PassengerFare> pfList = fb.get(0).getPassengerFareList();
if (pfList != null) {
List<PassengerSegmentFare> psfList = pfList.get(0).getPassengerSegmentFareList();
if (psfList != null) {
String carrierServiceClassDisplayName = psfList.get(0).getCarrierServiceClassDisplayName();
if (carrierServiceClassDisplayName != null) {
return carrierServiceClassDisplayName;
}
}
}
}
}
}
}
You see that's not THAT much of check once you reduce the verbosity of your code
inspired by kotlin safe call operator ?. & elivs opreator ?:, you can chain a custom SafeCallable. for example:
String serviceClass = SafeCallable.of(railsComponent)
.then(IRailsComponent::getRailOffer)
.then(RailOffer::getRailProducts)
.then(products -> products.get(0))
.then(...)
.orElse("");
SafeCallable
interface SafeCallable<T> {
T call();
static <T> SafeCallable<T> of(T value) {
return () -> value;
}
// ?. operator
default <R> SafeCallable<R> then(Function<T, R> step) {
return then(step, () -> null);
}
// ?: operator
default T orElse(T value) {
return then(identity(), () -> value).call();
}
default <R> SafeCallable<R> then(Function<T, R> step, Supplier<R> otherwise) {
T value = call();
return value == null ? otherwise::get : () -> step.apply(value);
}
}
If any of those nulls are rare, i would use a try catch block:
private String getRailsServiceClass(IRailsComponent railsComponent) {
try {
return railsComponent.getRailOffer().getRailProducts().get(0)
.getRailProduct().getFareBreakdownList().get(0).getPassengerFareList().get(0)
.getPassengerSegmentFareList().get(0).getCarrierServiceClassDisplayName();
} catch (NullPointerException e) {
return "";
}
}