Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

Collections no longer copied if marked non-null

Aperta
#4,118 0 commenti 0 reazioni 1 assegnatario Vedi su GitHub

@hduelme ci sta già lavorando.

Dal 27/8/2026.

Valutazione

Questa issue non è ancora stata valutata.

Descrizione

bug JSpecify
Expected behavior

When copying a non-null (JSpecify) collection property not only the null-check is skipped (correctly) but also the copy of the collection is not performed. This means that if the source object had an immutable collection, now so does the destination object. Previously, the destination was always mutable.

I'm not sure if this is an intended change of behaviour or a defect. If it's intended, it contradicts section 5.2 of the documentation which says:

If source and target attribute have the same type, the value will be simply copied direct from source to target. If the attribute is a collection (e.g. a List) a copy of the collection will be set into the target attribute.

Actual behavior

Assuming we have a class Bar:

@NullMarked
class Bar {
  private List<Foo> foos;
...

In previous versions, a List copy (where source and destination are both Bars) would use this code:

List<Foo> list = source.getFoos();
if ( list != null ) {
    destination.setFoos( new ArrayList<Foo>( list ) );
}

However, if getFoos() is marked as non-null, in 1.7.0.Beta2 we now get:

destination.setFoos( source.getFoos() );

This causes trouble for us because we actually use two MapStruct-generated methods as follows:

Bar copy(Bar source); // this used to leave `foos` mutable on the copied result
Bar patch(BarForm form, @MappingTarget Bar target); // this uses `clear()` and `addAll()` to modify `foos`

The workaround is either to add an explicit mapping to copy to force the collection-copying as per the docs:

@Mapping(target = "foos", expression = "java(new ArrayList<>(source.getFoos()))")

or to change the collection behaviour for the entire mapper using collectionMappingStrategy = CollectionMappingStrategy.TARGET_IMMUTABLE to stop it trying to clear() an immutable collection.

Steps to reproduce the problem
package mapstruct;

import java.util.List;
import org.jspecify.annotations.NullMarked;
import org.mapstruct.Mapper;
import org.mapstruct.MappingTarget;
import org.mapstruct.factory.Mappers;

public class BarMapperTest {

  public static void main(String[] args) {
    Bar bar = new Bar(); // immutable foos
    BarMapper mapper = Mappers.getMapper(BarMapper.class);

    Bar patchTarget = mapper.copy(bar);
    BarForm form = new BarForm(List.of(new Foo()));

    mapper.patch(form, patchTarget);
    System.out.println("Patched foos size: " + patchTarget.getFoos().size()); // Should print 1
  }
}

@Mapper
interface BarMapper {
  Bar copy(Bar source);

  Bar patch(BarForm form, @MappingTarget Bar target);
}

@NullMarked
class Bar {

  List<Foo> foos = List.of();

  public List<Foo> getFoos() {
    return foos;
  }

  public void setFoos(List<Foo> foos) {
    this.foos = foos;
  }
}

class Foo {}

class BarForm {
  List<Foo> foos;

  public BarForm(List<Foo> foos) {
    this.foos = foos;
  }

  public List<Foo> getFoos() {
    return foos;
  }
}

This blows up with:

Exception in thread "main" java.lang.UnsupportedOperationException
	at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:159)
	at java.base/java.util.ImmutableCollections$AbstractImmutableCollection.clear(ImmutableCollections.java:166)
	at mapstruct.BarMapperImpl.patch(BarMapperImpl.java:38)
	at mapstruct.BarMapperTest.main(BarMapperTest.java:18)

In version 1.6.3 it completes successfully, printing Patched foos size: 1

MapStruct Version

1.7.0.Beta2

Lingua principale
Java
Stelle
7.7k
Fork
1.1k
Metriche di merge delle PR
Nessuna PR unita negli ultimi 30g

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di mapstruct/mapstruct

Tutte le issue di mapstruct/mapstruct

Issue simili

Altre issue su Java

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.