#3852 Initialize Optionals with empty instead of null

This commit is contained in:
Dennis Melzer 2025-05-25 14:58:23 +02:00 committed by GitHub
parent 6e6fd01a2e
commit 05f27e96e2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 243 additions and 0 deletions

View File

@ -17,6 +17,10 @@ import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Optional;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.OptionalLong;
import java.util.Set; import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -367,6 +371,15 @@ public class Type extends ModelElement implements Comparable<Type> {
return componentType != null; return componentType != null;
} }
private boolean isType(Class<?> type) {
return type.getName().equals( getFullyQualifiedName() );
}
private boolean isOptionalType() {
return isType( Optional.class ) || isType( OptionalInt.class ) || isType( OptionalDouble.class ) ||
isType( OptionalLong.class );
}
public boolean isTypeVar() { public boolean isTypeVar() {
return (typeMirror.getKind() == TypeKind.TYPEVAR); return (typeMirror.getKind() == TypeKind.TYPEVAR);
} }
@ -1166,6 +1179,10 @@ public class Type extends ModelElement implements Comparable<Type> {
* FTL. * FTL.
*/ */
public String getNull() { public String getNull() {
if ( isOptionalType() ) {
return createReferenceName() + ".empty()";
}
if ( !isPrimitive() || isArrayType() ) { if ( !isPrimitive() || isArrayType() ) {
return "null"; return "null";
} }

View File

@ -0,0 +1,20 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.optional.nullvalue;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
* @author Dennis Melzer
*/
@Mapper
public interface OptionalDefaultMapper {
OptionalDefaultMapper INSTANCE = Mappers.getMapper( OptionalDefaultMapper.class );
Target map(Source source);
}

View File

@ -0,0 +1,53 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.optional.nullvalue;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.ProcessorTest;
import org.mapstruct.ap.testutil.WithClasses;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Dennis Melzer
*/
@WithClasses({
OptionalDefaultMapper.class,
Source.class,
Target.class
})
@IssueKey("3852")
public class OptionalDefaultMapperTest {
@ProcessorTest
public void shouldOptionalNotNull() {
Source source = new Source( null, null, null, null, null, null, null );
Target target = OptionalDefaultMapper.INSTANCE.map( source );
assertThat( target.getSomeString() ).isEmpty();
assertThat( target.getSomeInteger() ).isEmpty();
assertThat( target.getSomeDouble() ).isEmpty();
assertThat( target.getSomeBoolean() ).isEmpty();
assertThat( target.getSomeIntValue() ).isEmpty();
assertThat( target.getSomeDoubleValue() ).isEmpty();
assertThat( target.getSomeLongValue() ).isEmpty();
}
@ProcessorTest
public void shouldMapOptional() {
Source source = new Source( "someString", 10, 11D, Boolean.TRUE, 10, 100D, 200L );
Target target = OptionalDefaultMapper.INSTANCE.map( source );
assertThat( target.getSomeString() ).contains( "someString" );
assertThat( target.getSomeInteger() ).contains( 10 );
assertThat( target.getSomeDouble() ).contains( 11D );
assertThat( target.getSomeBoolean() ).contains( Boolean.TRUE );
assertThat( target.getSomeIntValue() ).hasValue( 10 );
assertThat( target.getSomeDoubleValue() ).hasValue( 100 );
assertThat( target.getSomeLongValue() ).hasValue( 200 );
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.optional.nullvalue;
import org.mapstruct.InjectionStrategy;
import org.mapstruct.Mapper;
import org.mapstruct.NullValueMappingStrategy;
import org.mapstruct.ReportingPolicy;
import org.mapstruct.ap.test.nestedproperties.simple._target.TargetObject;
import org.mapstruct.ap.test.nestedproperties.simple.source.SourceRoot;
import org.mapstruct.factory.Mappers;
/**
* @author Dennis Melzer
*/
@Mapper(
nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT,
injectionStrategy = InjectionStrategy.CONSTRUCTOR, unmappedTargetPolicy = ReportingPolicy.IGNORE
)
public interface SimpleConstructorMapper {
SimpleConstructorMapper MAPPER = Mappers.getMapper( SimpleConstructorMapper.class );
TargetObject toTargetObject(SourceRoot sourceRoot);
}

View File

@ -0,0 +1,61 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.optional.nullvalue;
import java.util.Optional;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.OptionalLong;
public class Source {
private final String someString;
private final Integer someInteger;
private final Double someDouble;
private final Boolean someBoolean;
private final Integer someIntValue;
private final Double someDoubleValue;
private final Long someLongValue;
public Source(String someString, Integer someInteger, Double someDouble, Boolean someBoolean, Integer someIntValue,
Double someDoubleValue, Long someLongValue) {
this.someString = someString;
this.someInteger = someInteger;
this.someDouble = someDouble;
this.someBoolean = someBoolean;
this.someIntValue = someIntValue;
this.someDoubleValue = someDoubleValue;
this.someLongValue = someLongValue;
}
public Optional<String> getSomeString() {
return Optional.ofNullable( someString );
}
public Optional<Integer> getSomeInteger() {
return Optional.ofNullable( someInteger );
}
public Optional<Double> getSomeDouble() {
return Optional.ofNullable( someDouble );
}
public Optional<Boolean> getSomeBoolean() {
return Optional.ofNullable( someBoolean );
}
public OptionalDouble getSomeDoubleValue() {
return someDouble != null ? OptionalDouble.of( someDoubleValue ) : OptionalDouble.empty();
}
public OptionalInt getSomeIntValue() {
return someIntValue != null ? OptionalInt.of( someIntValue ) : OptionalInt.empty();
}
public OptionalLong getSomeLongValue() {
return someLongValue != null ? OptionalLong.of( someLongValue ) : OptionalLong.empty();
}
}

View File

@ -0,0 +1,63 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.optional.nullvalue;
import java.util.Optional;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.OptionalLong;
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
public class Target {
private final Optional<String> someString;
private final Optional<Integer> someInteger;
private final Optional<Double> someDouble;
private final Optional<Boolean> someBoolean;
private final OptionalInt someIntValue;
private final OptionalDouble someDoubleValue;
private final OptionalLong someLongValue;
public Target(Optional<String> someString, Optional<Integer> someInteger, Optional<Double> someDouble,
Optional<Boolean> someBoolean, OptionalInt someIntValue, OptionalDouble someDoubleValue,
OptionalLong someLongValue) {
this.someString = someString;
this.someInteger = someInteger;
this.someDouble = someDouble;
this.someBoolean = someBoolean;
this.someIntValue = someIntValue;
this.someDoubleValue = someDoubleValue;
this.someLongValue = someLongValue;
}
public Optional<String> getSomeString() {
return someString;
}
public Optional<Integer> getSomeInteger() {
return someInteger;
}
public Optional<Double> getSomeDouble() {
return someDouble;
}
public Optional<Boolean> getSomeBoolean() {
return someBoolean;
}
public OptionalLong getSomeLongValue() {
return someLongValue;
}
public OptionalDouble getSomeDoubleValue() {
return someDoubleValue;
}
public OptionalInt getSomeIntValue() {
return someIntValue;
}
}