Skip to content

Defined mappings for subclass mappings with runtime exception subclass exhaustive strategy not working if result type is abstract class #3331

@xKrasusX

Description

@xKrasusX

Expected behavior

Mapstruct can map fields of abstract classes

Actual behavior

Mapstruct is not mapping fields of abstract classes - various problems occur

Steps to reproduce the problem

Source classes:

public abstract class Vehicle {
	private final String name;
	Vehicle(String name) {
		this.name = name;
	}
	public String getName() {
		return name;
	}
}

public class Car extends Vehicle {
	private final int numOfDoors;
	Car(String name, int numOfDoors) {
		super(name);
		this.numOfDoors = numOfDoors;
	}
	public int getNumOfDoors() {
		return numOfDoors;
	}
}

public class Motorbike extends Vehicle {
	private final boolean allowedForMinor;
	Motorbike(String name, boolean allowedForMinor) {
		super(name);
		this.allowedForMinor = allowedForMinor;
	}
	public boolean isAllowedForMinor() {
		return allowedForMinor;
	}
}

Target classes:

public abstract class VehicleDto {
	private final String name;
	public VehicleDto(String name) {
		this.name = name;
	}
}

public class CarDto extends VehicleDto {
	private final int numOfDoors;
	public CarDto(String name, int numOfDoors) {
		super(name);
		this.numOfDoors = numOfDoors;
	}
}

public class MotorbikeDto extends VehicleDto {
	private final boolean allowedForMinor;
	public MotorbikeDto(String name, boolean allowedForMinor) {
		super(name);
		this.allowedForMinor = allowedForMinor;
	}
}

Mapper config:

@Mapper(subclassExhaustiveStrategy = SubclassExhaustiveStrategy.RUNTIME_EXCEPTION)
public interface VehicleMapper {
	@SubclassMapping(target = CarDto.class, source = Car.class)
	@SubclassMapping(target = MotorbikeDto.class, source = Motorbike.class)
	VehicleDto mapToDto(Vehicle vehicle);
}

Everything works fine so far, the mapper class is generated and mapping correctly all the fields.

The problem occurs when I'm trying to add anything related to name field in VehicleDto, for example:

@Mapper(subclassExhaustiveStrategy = SubclassExhaustiveStrategy.RUNTIME_EXCEPTION)
public interface VehicleMapper {
	@SubclassMapping(target = CarDto.class, source = Car.class)
	@SubclassMapping(target = MotorbikeDto.class, source = Motorbike.class)
	@Mapping(target = "name", constant = "noname") // <-------- added this one --------
	VehicleDto mapToDto(Vehicle vehicle);
}

In this case I'm getting a compilation error:

Unknown property "name" in result type VehicleDto. Did you mean "null"?

If I also add getter to the abstract target class:

public abstract class VehicleDto {
	private final String name;
	public VehicleDto(String name) {
		this.name = name;
	}
	public String getName() {
		return name;
	}
}

It's still not working but the build error is different:

Property "name" has no write accessor in VehicleDto.

Isn't Mapstruct supposed to handle both these cases correctly and map the name field?

I'm using Java 17, Spring Boot 3.1.1, no Lombok.

MapStruct Version

1.5.5.Final

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions