#3231 Prefer record constructor annotated with @Default

This commit is contained in:
Oliver Erhart 2023-05-24 06:04:13 +02:00 committed by GitHub
parent 84c443df9c
commit 51f4e7eba9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 107 additions and 1 deletions

View File

@ -0,0 +1,21 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.itest.records;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author Filip Hrisafov
*/
@Documented
@Target(ElementType.CONSTRUCTOR)
@Retention(RetentionPolicy.SOURCE)
public @interface Default {
}

View File

@ -0,0 +1,15 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.itest.records;
import java.util.List;
/**
* @author Oliver Erhart
*/
public record Task( String id, Long number ) {
}

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.itest.records;
import java.util.List;
/**
* @author Oliver Erhart
*/
public record TaskDto(String id, Long number) {
@Default
TaskDto(String id) {
this( id, 1L );
}
}

View File

@ -0,0 +1,22 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.itest.records;
import org.mapstruct.Mapper;
import org.mapstruct.ReportingPolicy;
import org.mapstruct.factory.Mappers;
/**
* @author Oliver Erhart
*/
@Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR)
public interface TaskMapper {
TaskMapper INSTANCE = Mappers.getMapper( TaskMapper.class );
TaskDto toRecord(Task source);
}

View File

@ -83,4 +83,16 @@ public class RecordsTest {
assertThat( value.isActive() ).isEqualTo( false );
assertThat( value.premium() ).isEqualTo( true );
}
@Test
public void shouldUseDefaultConstructor() {
Task entity = new Task( "some-id", 1000L );
TaskDto value = TaskMapper.INSTANCE.toRecord( entity );
assertThat( value ).isNotNull();
assertThat( value.id() ).isEqualTo( "some-id" );
assertThat( value.number() ).isEqualTo( 1L );
}
}

View File

@ -791,7 +791,23 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
}
if ( type.isRecord() ) {
// If the type is a record then just get the record components and use then
List<ExecutableElement> constructors = ElementFilter.constructorsIn( type.getTypeElement()
.getEnclosedElements() );
for ( ExecutableElement constructor : constructors ) {
if ( constructor.getModifiers().contains( Modifier.PRIVATE ) ) {
continue;
}
// prefer constructor annotated with @Default
if ( hasDefaultAnnotationFromAnyPackage( constructor ) ) {
return getConstructorAccessor( type, constructor );
}
}
// Other than that, just get the record components and use them
List<Element> recordComponents = type.getRecordComponents();
List<ParameterBinding> parameterBindings = new ArrayList<>( recordComponents.size() );
Map<String, Accessor> constructorAccessors = new LinkedHashMap<>();