Skip to content

Commit 65c4726

Browse files
committed
Let composite/filtered collections accept null elements
1. Keep `FilteredIterator` consistent with `CompositeIterator` to accept `null` 2. Fix warning `Non-null type argument is expected` and `Assigning a class with nullable type arguments when a class with not-null type arguments is expected` Signed-off-by: Yanming Zhou <zhouyanming@gmail.com>
1 parent 0c60266 commit 65c4726

16 files changed

Lines changed: 128 additions & 16 deletions

spring-core/src/main/java/org/springframework/util/CompositeCollection.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,11 @@
2828
* exposed through {@link CompositeMap#values()}.
2929
*
3030
* @author Arjen Poutsma
31+
* @author Yanming Zhou
3132
* @since 6.2
3233
* @param <E> the type of elements maintained by this collection
3334
*/
34-
class CompositeCollection<E> implements Collection<E> {
35+
class CompositeCollection<E extends @Nullable Object> implements Collection<E> {
3536

3637
private final Collection<E> first;
3738

spring-core/src/main/java/org/springframework/util/CompositeIterator.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.util;
1818

19+
import org.jspecify.annotations.Nullable;
20+
1921
import java.util.Iterator;
2022
import java.util.LinkedHashSet;
2123
import java.util.NoSuchElementException;
@@ -30,10 +32,11 @@
3032
*
3133
* @author Erwin Vervaet
3234
* @author Juergen Hoeller
35+
* @author Yanming Zhou
3336
* @since 3.0
3437
* @param <E> the element type
3538
*/
36-
public class CompositeIterator<E> implements Iterator<E> {
39+
public class CompositeIterator<E extends @Nullable Object> implements Iterator<E> {
3740

3841
private final Set<Iterator<E>> iterators = new LinkedHashSet<>();
3942

spring-core/src/main/java/org/springframework/util/CompositeMap.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,18 @@
3232
* {@link CollectionUtils#compositeMap(Map, Map, BiFunction, Consumer)}.
3333
*
3434
* @author Arjen Poutsma
35+
* @author Yanming Zhou
3536
* @since 6.2
3637
* @param <K> the type of keys maintained by this map
3738
* @param <V> the type of mapped values
3839
*/
39-
final class CompositeMap<K, V> implements Map<K, V> {
40+
final class CompositeMap<K, V extends @Nullable Object> implements Map<K, V> {
4041

41-
private final Map<K,V> first;
42+
private final Map<K, V> first;
4243

43-
private final Map<K,V> second;
44+
private final Map<K, V> second;
4445

45-
private final @Nullable BiFunction<K,V,V> putFunction;
46+
private final @Nullable BiFunction<K, V, V> putFunction;
4647

4748
private final @Nullable Consumer<Map<K, V>> putAllFunction;
4849

@@ -53,7 +54,7 @@ final class CompositeMap<K, V> implements Map<K, V> {
5354

5455
CompositeMap(Map<K, V> first, Map<K, V> second,
5556
@Nullable BiFunction<K, V, V> putFunction,
56-
@Nullable Consumer<Map<K,V>> putAllFunction) {
57+
@Nullable Consumer<Map<K, V>> putAllFunction) {
5758

5859
Assert.notNull(first, "First must not be null");
5960
Assert.notNull(second, "Second must not be null");
@@ -106,7 +107,7 @@ public boolean containsValue(Object value) {
106107
}
107108

108109
@Override
109-
public @Nullable V put(K key, V value) {
110+
public V put(K key, V value) {
110111
if (this.putFunction == null) {
111112
throw new UnsupportedOperationException();
112113
}
@@ -116,7 +117,7 @@ public boolean containsValue(Object value) {
116117
}
117118

118119
@Override
119-
public @Nullable V remove(Object key) {
120+
public V remove(Object key) {
120121
V firstResult = this.first.remove(key);
121122
V secondResult = this.second.remove(key);
122123
if (firstResult != null) {

spring-core/src/main/java/org/springframework/util/CompositeSet.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,11 @@
2525
* {@link CompositeMap#keySet()} and {@link CompositeMap#entrySet()}.
2626
*
2727
* @author Arjen Poutsma
28+
* @author Yanming Zhou
2829
* @since 6.2
2930
* @param <E> the type of elements maintained by this set
3031
*/
31-
final class CompositeSet<E> extends CompositeCollection<E> implements Set<E> {
32+
final class CompositeSet<E extends @Nullable Object> extends CompositeCollection<E> implements Set<E> {
3233

3334
CompositeSet(Set<E> first, Set<E> second) {
3435
super(first, second);

spring-core/src/main/java/org/springframework/util/FilteredCollection.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.util;
1818

19+
import org.jspecify.annotations.Nullable;
20+
1921
import java.util.AbstractCollection;
2022
import java.util.Collection;
2123
import java.util.Iterator;
@@ -26,10 +28,11 @@
2628
* This type is used by {@link CompositeMap}.
2729
*
2830
* @author Arjen Poutsma
31+
* @author Yanming Zhou
2932
* @since 6.2
3033
* @param <E> the type of elements maintained by this collection
3134
*/
32-
class FilteredCollection<E> extends AbstractCollection<E> {
35+
class FilteredCollection<E extends @Nullable Object> extends AbstractCollection<E> {
3336

3437
private final Collection<E> delegate;
3538

spring-core/src/main/java/org/springframework/util/FilteredIterator.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,11 @@
2828
* <p>This type is used by {@link CompositeMap}.
2929
*
3030
* @author Arjen Poutsma
31+
* @author Yanming Zhou
3132
* @since 6.2
3233
* @param <E> the type of elements returned by this iterator
3334
*/
34-
final class FilteredIterator<E> implements Iterator<E> {
35+
final class FilteredIterator<E extends @Nullable Object> implements Iterator<E> {
3536

3637
private final Iterator<E> delegate;
3738

@@ -56,12 +57,11 @@ public boolean hasNext() {
5657
}
5758

5859
@Override
59-
public E next() {
60+
public @Nullable E next() {
6061
if (!this.hasNext && !setNext()) {
6162
throw new NoSuchElementException();
6263
}
6364
this.hasNext = false;
64-
Assert.state(this.next != null, "Next should not be null");
6565
return this.next;
6666
}
6767

spring-core/src/main/java/org/springframework/util/FilteredMap.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,12 @@
2828
* This type is used by {@link CompositeMap}.
2929
*
3030
* @author Arjen Poutsma
31+
* @author Yanming Zhou
3132
* @since 6.2
3233
* @param <K> the type of keys maintained by this map
3334
* @param <V> the type of mapped values
3435
*/
35-
final class FilteredMap<K, V> extends AbstractMap<K, V> {
36+
final class FilteredMap<K, V extends @Nullable Object> extends AbstractMap<K, V> {
3637

3738
private final Map<K, V> delegate;
3839

spring-core/src/main/java/org/springframework/util/FilteredSet.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,11 @@
2626
* This type is used by {@link CompositeMap}.
2727
*
2828
* @author Arjen Poutsma
29+
* @author Yanming Zhou
2930
* @since 6.2
3031
* @param <E> the type of elements maintained by this set
3132
*/
32-
final class FilteredSet<E> extends FilteredCollection<E> implements Set<E> {
33+
final class FilteredSet<E extends @Nullable Object> extends FilteredCollection<E> implements Set<E> {
3334

3435
public FilteredSet(Set<E> delegate, Predicate<E> filter) {
3536
super(delegate, filter);

spring-core/src/test/java/org/springframework/util/CompositeCollectionTests.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,15 @@
2121
import java.util.Iterator;
2222
import java.util.List;
2323

24+
import org.jspecify.annotations.Nullable;
2425
import org.junit.jupiter.api.Test;
2526

2627
import static org.assertj.core.api.Assertions.assertThat;
2728
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
2829

2930
/**
3031
* @author Arjen Poutsma
32+
* @author Yanming Zhou
3133
*/
3234
class CompositeCollectionTests {
3335

@@ -192,4 +194,17 @@ void clear() {
192194
assertThat(first).isEmpty();
193195
assertThat(second).isEmpty();
194196
}
197+
198+
@Test
199+
void nullable() {
200+
List<@Nullable String> first = new ArrayList<>();
201+
first.add("foo");
202+
first.add(null);
203+
List<@Nullable String> second = new ArrayList<>();
204+
second.add("bar");
205+
second.add(null);
206+
CompositeCollection<@Nullable String> composite = new CompositeCollection<>(first, second);
207+
208+
assertThat(composite).containsExactly("foo", null, "bar", null);
209+
}
195210
}

spring-core/src/test/java/org/springframework/util/CompositeIteratorTests.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.List;
2222
import java.util.NoSuchElementException;
2323

24+
import org.jspecify.annotations.Nullable;
2425
import org.junit.jupiter.api.Test;
2526

2627
import static org.assertj.core.api.Assertions.assertThat;
@@ -33,6 +34,7 @@
3334
*
3435
* @author Erwin Vervaet
3536
* @author Juergen Hoeller
37+
* @author Yanming Zhou
3638
*/
3739
class CompositeIteratorTests {
3840

@@ -99,4 +101,13 @@ void duplicateIterators() {
99101
it.add(iterator));
100102
}
101103

104+
@Test
105+
void nullable() {
106+
List<@Nullable String> first = Arrays.asList("1", null);
107+
List<@Nullable String> second = Arrays.asList("2", null);
108+
CompositeIterator<String> it = new CompositeIterator<>();
109+
it.add(first.iterator());
110+
it.add(second.iterator());
111+
assertThat(it).toIterable().containsExactly("1", null, "2", null);
112+
}
102113
}

0 commit comments

Comments
 (0)