Skip to content

Commit c406cb9

Browse files
authored
refactor(api): disallow null keys or values (#36)
* refactor: disallow null keys or values * refactor: require non-null ICacheSpec provider * chore(caffeine3): rename delegate class * fix(expiringmap): ensure value is non null * chore: prepare version for stable release
1 parent 251bab3 commit c406cb9

File tree

17 files changed

+130
-80
lines changed

17 files changed

+130
-80
lines changed

api/src/main/java/io/github/xanthic/cache/api/Cache.java

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* This interface can be thought as a superset of {@link java.util.Map};
1414
* less functionality is required here to achieve greater compatibility.
1515
* <p>
16-
* There cannot be duplicate keys and null values are not necessarily supported.
16+
* There cannot be duplicate keys, and null keys or values are not permitted.
1717
* <p>
1818
* Instances should be thread-safe, but no guarantee is made whether
1919
* this is achieved through fine-grained locking or blunt synchronization.
@@ -30,10 +30,10 @@ public interface Cache<K, V> {
3030
*
3131
* @param key the key whose mapped value should be queried
3232
* @return the value associated with the key in the cache, or null if no such mapping was found
33-
* @throws NullPointerException if the specified key is null and this implementation does not permit null keys
33+
* @throws NullPointerException if the specified key is null
3434
*/
3535
@Nullable
36-
V get(K key);
36+
V get(@NotNull K key);
3737

3838
/**
3939
* Associates the specified key with the specified value,
@@ -42,20 +42,20 @@ public interface Cache<K, V> {
4242
* @param key the key whose mapping should be created or updated
4343
* @param value the value to be associated with the specified key
4444
* @return the previous value associated with the key, or null if no prior mapping existed
45-
* @throws NullPointerException if the specified key or value is null and this cache does not permit null keys or values
45+
* @throws NullPointerException if the specified key or value is null
4646
*/
4747
@Nullable
48-
V put(K key, V value);
48+
V put(@NotNull K key, @NotNull V value);
4949

5050
/**
5151
* Deletes any mapping that may exist for the specified key.
5252
*
5353
* @param key the key whose mapping should be deleted
5454
* @return the value in the removed mapping, or null if no mapping existed
55-
* @throws NullPointerException if the specified key is null and this cache does not permit null keys
55+
* @throws NullPointerException if the specified key is null
5656
*/
5757
@Nullable
58-
V remove(K key);
58+
V remove(@NotNull K key);
5959

6060
/**
6161
* Removes all entries from the cache.
@@ -73,11 +73,11 @@ public interface Cache<K, V> {
7373
* @param key the key with which the specified value is to be associated
7474
* @param computeFunc the function to compute a value
7575
* @return the new value associated with the specified key, or null if none
76-
* @throws NullPointerException if the specified key is null and this cache does not support null keys, or the compute function is null
76+
* @throws NullPointerException if the specified key is null or the compute function is null
7777
* @implNote atomicity is dependent on provider characteristics
7878
*/
7979
@Nullable
80-
V compute(K key, @NotNull BiFunction<? super K, ? super V, ? extends V> computeFunc);
80+
V compute(@NotNull K key, @NotNull BiFunction<? super K, ? super V, ? extends V> computeFunc);
8181

8282
/**
8383
* Obtains the value currently associated with the specified key,
@@ -86,9 +86,9 @@ public interface Cache<K, V> {
8686
* @param key the key whose mapping should be created or returned
8787
* @param computeFunc the value supplier for a given key, if no mapping already existed
8888
* @return the current (existing or computed) value associated with the key
89-
* @throws NullPointerException if the specified key is null and this cache does not permit null keys, or the compute function is null
89+
* @throws NullPointerException if the specified key is null or the compute function is null
9090
*/
91-
V computeIfAbsent(K key, @NotNull Function<K, V> computeFunc);
91+
V computeIfAbsent(@NotNull K key, @NotNull Function<K, V> computeFunc);
9292

9393
/**
9494
* Computes a new value for a specific key, if a mapping already existed.
@@ -98,10 +98,10 @@ public interface Cache<K, V> {
9898
* @param key the key whose mapping should be updated
9999
* @param computeFunc the function to compute the new value for an already existing mapping
100100
* @return the new value associated with the key, or null if none
101-
* @throws NullPointerException if the specified key is null and this cache does not support null keys, or the compute function is null
101+
* @throws NullPointerException if the specified key is null or the compute function is null
102102
*/
103103
@Nullable
104-
V computeIfPresent(K key, @NotNull BiFunction<? super K, ? super V, ? extends V> computeFunc);
104+
V computeIfPresent(@NotNull K key, @NotNull BiFunction<? super K, ? super V, ? extends V> computeFunc);
105105

106106
/**
107107
* Creates a mapping from the specified key to the specified value,
@@ -110,10 +110,10 @@ public interface Cache<K, V> {
110110
* @param key the key whose mapping should be created or returned
111111
* @param value the value that should be associated with the key if no prior mapping exists
112112
* @return the previous value associated with the key, or null if no mapping already existed
113-
* @throws NullPointerException if the specified key or value is null and this cache does not permit null keys or values
113+
* @throws NullPointerException if the specified key or value is null
114114
*/
115115
@Nullable
116-
V putIfAbsent(K key, V value);
116+
V putIfAbsent(@NotNull K key, @NotNull V value);
117117

118118
/**
119119
* Associates the key with the specified value (or the result of the atomic merge function if a mapping already existed).
@@ -122,19 +122,19 @@ public interface Cache<K, V> {
122122
* @param value the value to be associated with the key or merged with the existing mapped value
123123
* @param mergeFunc the function that takes the existing value and the new value to compute a merged value
124124
* @return the latest value associated with the specified key
125-
* @throws NullPointerException if the specified key is null and this cache does not support null keys or the value or mergeFunc is null
125+
* @throws NullPointerException if the specified key or the value or mergeFunc is null
126126
*/
127-
V merge(K key, V value, @NotNull BiFunction<V, V, V> mergeFunc);
127+
V merge(@NotNull K key, @NotNull V value, @NotNull BiFunction<V, V, V> mergeFunc);
128128

129129
/**
130130
* Replaces the entry for the specified key only if it is currently mapped to some value.
131131
*
132132
* @param key the key whose mapped value should be updated
133133
* @param value the value to be associated with the specified key
134134
* @return whether a replacement occurred (a prior mapping must have existed to return true)
135-
* @throws NullPointerException if the specified key or value is null, and this cache does not permit null keys or values
135+
* @throws NullPointerException if the specified key or value is null
136136
*/
137-
boolean replace(K key, V value);
137+
boolean replace(@NotNull K key, @NotNull V value);
138138

139139
/**
140140
* Replaces the value for the specified key only if it is currently mapped to the specified old value.
@@ -143,15 +143,15 @@ public interface Cache<K, V> {
143143
* @param oldValue the value expected to be already associated with the specified key
144144
* @param newValue the value to be newly associated with the specified key
145145
* @return whether a replacement occurred
146-
* @throws NullPointerException if a specified key or newValue is null, and this cache does not permit null keys or values
146+
* @throws NullPointerException if a specified key or newValue is null
147147
*/
148-
boolean replace(K key, V oldValue, V newValue);
148+
boolean replace(@NotNull K key, @NotNull V oldValue, @NotNull V newValue);
149149

150150
/**
151151
* Copies all of the mappings from the specified map to this cache.
152152
*
153153
* @param map the map whose entries should be added to this cache
154-
* @throws NullPointerException if the specified map is null, or if this cache does not permit null keys or values, and the specified map contains null keys or values
154+
* @throws NullPointerException if the map is null, or the specified map contains null keys or values
155155
* @implNote There is no behavior guarantee when there are concurrent updates to the map
156156
*/
157157
default void putAll(@NotNull Map<? extends K, ? extends V> map) {

api/src/main/java/io/github/xanthic/cache/api/ICacheSpec.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.github.xanthic.cache.api;
22

33
import io.github.xanthic.cache.api.domain.ExpiryType;
4+
import org.jetbrains.annotations.NotNull;
45
import org.jetbrains.annotations.Nullable;
56

67
import java.time.Duration;
@@ -21,7 +22,7 @@ public interface ICacheSpec<K, V> {
2122
*
2223
* @return the specific provider that should be used to fulfill the desired cache specification
2324
*/
24-
@Nullable
25+
@NotNull
2526
CacheProvider provider();
2627

2728
/**

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ allprojects {
1414
}
1515

1616
group = "io.github.xanthic.cache"
17-
version = "1.0.0-SNAPSHOT"
17+
version = "0.1.0"
1818
}
1919

2020
subprojects {

core/src/main/java/io/github/xanthic/cache/core/AbstractCache.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
public abstract class AbstractCache<K, V> implements Cache<K, V> {
2929

3030
@Override
31-
public V computeIfAbsent(K key, @NotNull Function<K, V> computeFunc) {
31+
public V computeIfAbsent(@NotNull K key, @NotNull Function<K, V> computeFunc) {
3232
synchronized (getLock()) {
3333
V old = this.get(key);
3434
if (old != null) return old;
@@ -40,7 +40,7 @@ public V computeIfAbsent(K key, @NotNull Function<K, V> computeFunc) {
4040

4141
@Nullable
4242
@Override
43-
public V compute(K key, @NotNull BiFunction<? super K, ? super V, ? extends V> computeFunc) {
43+
public V compute(@NotNull K key, @NotNull BiFunction<? super K, ? super V, ? extends V> computeFunc) {
4444
synchronized (getLock()) {
4545
V oldValue = this.get(key);
4646
V newValue = computeFunc.apply(key, oldValue);
@@ -55,7 +55,7 @@ public V compute(K key, @NotNull BiFunction<? super K, ? super V, ? extends V> c
5555
}
5656

5757
@Override
58-
public V computeIfPresent(K key, @NotNull BiFunction<? super K, ? super V, ? extends V> computeFunc) {
58+
public V computeIfPresent(@NotNull K key, @NotNull BiFunction<? super K, ? super V, ? extends V> computeFunc) {
5959
synchronized (getLock()) {
6060
V oldValue = this.get(key);
6161
if (oldValue != null) {
@@ -72,7 +72,7 @@ public V computeIfPresent(K key, @NotNull BiFunction<? super K, ? super V, ? ext
7272
}
7373

7474
@Override
75-
public V putIfAbsent(K key, V value) {
75+
public V putIfAbsent(@NotNull K key, @NotNull V value) {
7676
synchronized (getLock()) {
7777
V old = this.get(key);
7878
if (old == null) {
@@ -83,7 +83,7 @@ public V putIfAbsent(K key, V value) {
8383
}
8484

8585
@Override
86-
public V merge(K key, V value, @NotNull BiFunction<V, V, V> mergeFunc) {
86+
public V merge(@NotNull K key, @NotNull V value, @NotNull BiFunction<V, V, V> mergeFunc) {
8787
synchronized (getLock()) {
8888
V old = putIfAbsent(key, value);
8989
if (old == null) return value;
@@ -94,7 +94,7 @@ public V merge(K key, V value, @NotNull BiFunction<V, V, V> mergeFunc) {
9494
}
9595

9696
@Override
97-
public boolean replace(K key, V value) {
97+
public boolean replace(@NotNull K key, @NotNull V value) {
9898
synchronized (getLock()) {
9999
V old = this.get(key);
100100
if (old == null) return false;
@@ -104,7 +104,8 @@ public boolean replace(K key, V value) {
104104
}
105105

106106
@Override
107-
public boolean replace(K key, V oldValue, V newValue) {
107+
public boolean replace(@NotNull K key, @NotNull V oldValue, @NotNull V newValue) {
108+
// noinspection ConstantConditions
108109
if (oldValue == null) return false;
109110
synchronized (getLock()) {
110111
if (Objects.equals(oldValue, this.get(key))) {

core/src/main/java/io/github/xanthic/cache/core/CacheApiSpec.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,16 @@ public void validate() {
7272
* @return CacheApiSpec
7373
* @throws NoDefaultCacheImplementationException if a provider is not specified and no default provider has been set
7474
* @throws MisconfiguredCacheException if the cache settings are invalid (e.g., negative max size or expiry time)
75+
* @implNote During the building stage, the provider may not be initialized to a non-null value.
7576
*/
7677
@NotNull
7778
public static <K, V> CacheApiSpec<K, V> process(@NotNull Consumer<CacheApiSpec<K, V>> spec) {
7879
CacheApiSpec<K, V> data = new CacheApiSpec<>();
7980
spec.accept(data);
8081

81-
// set / init default cache provider if nothing is set
82+
// noinspection ConstantConditions
8283
if (data.provider() == null) {
84+
// set / init default cache provider if nothing is set
8385
data.provider(CacheApiSettings.getInstance().getDefaultCacheProvider());
8486
log.debug("No cache provider explicitly specified; cache defaults to {}!", data.provider.getClass().getCanonicalName());
8587
}

core/src/main/java/io/github/xanthic/cache/core/delegate/GenericMapCacheDelegate.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,33 +24,33 @@ public class GenericMapCacheDelegate<K, V> implements Cache<K, V> {
2424
private final Map<K, V> map;
2525

2626
@Override
27-
public V get(K key) {
27+
public V get(@NotNull K key) {
2828
return map.get(key);
2929
}
3030

3131
@Override
32-
public V put(K key, V value) {
32+
public V put(@NotNull K key, @NotNull V value) {
3333
return map.put(key, value);
3434
}
3535

3636
@Nullable
3737
@Override
38-
public V compute(K key, @NotNull BiFunction<? super K, ? super V, ? extends V> computeFunc) {
38+
public V compute(@NotNull K key, @NotNull BiFunction<? super K, ? super V, ? extends V> computeFunc) {
3939
return map.compute(key, computeFunc);
4040
}
4141

4242
@Override
43-
public V computeIfAbsent(K key, @NotNull Function<K, V> computeFunc) {
43+
public V computeIfAbsent(@NotNull K key, @NotNull Function<K, V> computeFunc) {
4444
return map.computeIfAbsent(key, computeFunc);
4545
}
4646

4747
@Override
48-
public V computeIfPresent(K key, @NotNull BiFunction<? super K, ? super V, ? extends V> computeFunc) {
48+
public V computeIfPresent(@NotNull K key, @NotNull BiFunction<? super K, ? super V, ? extends V> computeFunc) {
4949
return map.computeIfPresent(key, computeFunc);
5050
}
5151

5252
@Override
53-
public V remove(K key) {
53+
public V remove(@NotNull K key) {
5454
return map.remove(key);
5555
}
5656

@@ -65,22 +65,22 @@ public long size() {
6565
}
6666

6767
@Override
68-
public V putIfAbsent(K key, V value) {
68+
public V putIfAbsent(@NotNull K key, @NotNull V value) {
6969
return map.putIfAbsent(key, value);
7070
}
7171

7272
@Override
73-
public V merge(K key, V value, @NotNull BiFunction<V, V, V> mergeFunc) {
73+
public V merge(@NotNull K key, @NotNull V value, @NotNull BiFunction<V, V, V> mergeFunc) {
7474
return map.merge(key, value, mergeFunc);
7575
}
7676

7777
@Override
78-
public boolean replace(K key, V value) {
78+
public boolean replace(@NotNull K key, @NotNull V value) {
7979
return map.replace(key, value) != null;
8080
}
8181

8282
@Override
83-
public boolean replace(K key, V oldValue, V newValue) {
83+
public boolean replace(@NotNull K key, @NotNull V oldValue, @NotNull V newValue) {
8484
return map.replace(key, oldValue, newValue);
8585
}
8686

provider-androidx/src/main/java/io/github/xanthic/cache/provider/androidx/ExpiringLruDelegate.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ protected void entryRemoved(boolean evicted, @NotNull K key, @NotNull V oldValue
6060
};
6161

6262
@Override
63-
public V get(K key) {
63+
public V get(@NotNull K key) {
6464
synchronized (getLock()) {
6565
V v = cache.get(key);
6666
if (type == ExpiryType.POST_ACCESS) start(key, v);
@@ -69,7 +69,7 @@ public V get(K key) {
6969
}
7070

7171
@Override
72-
public V put(K key, V value) {
72+
public V put(@NotNull K key, @NotNull V value) {
7373
synchronized (getLock()) {
7474
V prev = cache.put(key, value);
7575
start(key, value);
@@ -80,7 +80,7 @@ public V put(K key, V value) {
8080
}
8181

8282
@Override
83-
public V remove(K key) {
83+
public V remove(@NotNull K key) {
8484
synchronized (getLock()) {
8585
cancelIfRunning(key, cache.get(key)); // mapping is being removed already
8686
return cache.remove(key);

provider-androidx/src/main/java/io/github/xanthic/cache/provider/androidx/LruDelegate.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,18 @@ class LruDelegate<K, V> extends AbstractCache<K, V> {
1515
LruCache<K, V> cache;
1616

1717
@Override
18-
public V get(K key) {
18+
public V get(@NotNull K key) {
1919
// note: underlying operations are synchronized on same object
2020
return cache.get(key);
2121
}
2222

2323
@Override
24-
public V put(K key, V value) {
24+
public V put(@NotNull K key, @NotNull V value) {
2525
return cache.put(key, value);
2626
}
2727

2828
@Override
29-
public V remove(K key) {
29+
public V remove(@NotNull K key) {
3030
return cache.remove(key);
3131
}
3232

0 commit comments

Comments
 (0)