From 2547e169381f90df57b735be313ffc98ac357583 Mon Sep 17 00:00:00 2001
From: Gregory Oschwald
Date: Tue, 26 Aug 2025 14:33:19 -0700
Subject: [PATCH 1/9] Drop Java 11 support, require Java 17
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Update Maven compiler plugin to use Java 17
- Update GitHub Actions CI to test Java 17, 21, and 24
- Update README requirements
- Update CHANGELOG for version 4.0.0
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude
---
.github/workflows/test.yml | 2 +-
CHANGELOG.md | 5 +++++
README.md | 2 +-
pom.xml | 6 +++---
4 files changed, 10 insertions(+), 5 deletions(-)
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 76163831..e797c2f0 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -9,7 +9,7 @@ jobs:
matrix:
distribution: ['zulu']
os: [ubuntu-latest, windows-latest, macos-latest]
- version: [ 11, 17, 21, 22 ]
+ version: [ 17, 21, 24 ]
steps:
- uses: actions/checkout@v5
with:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9f650c70..7b80d986 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,11 @@
CHANGELOG
=========
+4.0.0
+------------------
+
+* Java 17 or greater is now required.
+
3.2.0 (2025-05-28)
------------------
diff --git a/README.md b/README.md
index c55f0f87..b4744786 100644
--- a/README.md
+++ b/README.md
@@ -207,7 +207,7 @@ specific to this reader, please [contact MaxMind support](https://www.maxmind.co
## Requirements ##
-This API requires Java 11 or greater.
+This API requires Java 17 or greater.
## Contributing ##
diff --git a/pom.xml b/pom.xml
index b8d95115..70c31ea9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -147,9 +147,9 @@
maven-compiler-plugin
3.14.0
- 11
- 11
- 11
+ 17
+ 17
+ 17
From 14dc092c60a464b173e3f1edc09e591c020f294b Mon Sep 17 00:00:00 2001
From: Gregory Oschwald
Date: Tue, 26 Aug 2025 14:36:35 -0700
Subject: [PATCH 2/9] Convert CtrlData to record
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Converts the CtrlData class to use Java 17 records, reducing boilerplate
and improving memory layout. Updates all getter method calls to use
record accessors.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude
---
src/main/java/com/maxmind/db/CtrlData.java | 29 +---------------------
src/main/java/com/maxmind/db/Decoder.java | 8 +++---
2 files changed, 5 insertions(+), 32 deletions(-)
diff --git a/src/main/java/com/maxmind/db/CtrlData.java b/src/main/java/com/maxmind/db/CtrlData.java
index c9bf03f1..ff9d0ce9 100644
--- a/src/main/java/com/maxmind/db/CtrlData.java
+++ b/src/main/java/com/maxmind/db/CtrlData.java
@@ -1,31 +1,4 @@
package com.maxmind.db;
-final class CtrlData {
- private final Type type;
- private final int ctrlByte;
- private final int offset;
- private final int size;
-
- CtrlData(Type type, int ctrlByte, int offset, int size) {
- this.type = type;
- this.ctrlByte = ctrlByte;
- this.offset = offset;
- this.size = size;
- }
-
- public Type getType() {
- return this.type;
- }
-
- public int getCtrlByte() {
- return this.ctrlByte;
- }
-
- public int getOffset() {
- return this.offset;
- }
-
- public int getSize() {
- return this.size;
- }
+record CtrlData(Type type, int ctrlByte, int offset, int size) {
}
diff --git a/src/main/java/com/maxmind/db/Decoder.java b/src/main/java/com/maxmind/db/Decoder.java
index ab9a0218..d8bca62e 100644
--- a/src/main/java/com/maxmind/db/Decoder.java
+++ b/src/main/java/com/maxmind/db/Decoder.java
@@ -495,11 +495,11 @@ private int nextValueOffset(int offset, int numberToSkip)
}
CtrlData ctrlData = this.getCtrlData(offset);
- int ctrlByte = ctrlData.getCtrlByte();
- int size = ctrlData.getSize();
- offset = ctrlData.getOffset();
+ int ctrlByte = ctrlData.ctrlByte();
+ int size = ctrlData.size();
+ offset = ctrlData.offset();
- Type type = ctrlData.getType();
+ Type type = ctrlData.type();
switch (type) {
case POINTER:
int pointerSize = ((ctrlByte >>> 3) & 0x3) + 1;
From 4ddd7ec0cc8eb2fe417e26c88fddf499cd0cdd28 Mon Sep 17 00:00:00 2001
From: Gregory Oschwald
Date: Tue, 26 Aug 2025 14:37:42 -0700
Subject: [PATCH 3/9] Convert CacheKey to record
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Converts the CacheKey class to use Java 17 records, reducing boilerplate
and improving memory layout. Updates all getter method calls to use
record accessors and adds proper Javadoc for record parameters.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude
---
src/main/java/com/maxmind/db/CacheKey.java | 60 ++--------------------
src/main/java/com/maxmind/db/Decoder.java | 6 +--
2 files changed, 7 insertions(+), 59 deletions(-)
diff --git a/src/main/java/com/maxmind/db/CacheKey.java b/src/main/java/com/maxmind/db/CacheKey.java
index d62c0843..3a2c0d46 100644
--- a/src/main/java/com/maxmind/db/CacheKey.java
+++ b/src/main/java/com/maxmind/db/CacheKey.java
@@ -6,61 +6,9 @@
* of the value.
*
* @param the type of value
+ * @param offset the offset of the value in the database file
+ * @param cls the class of the value
+ * @param type the type of the value
*/
-public final class CacheKey {
- private final int offset;
- private final Class cls;
- private final java.lang.reflect.Type type;
-
- CacheKey(int offset, Class cls, java.lang.reflect.Type type) {
- this.offset = offset;
- this.cls = cls;
- this.type = type;
- }
-
- int getOffset() {
- return this.offset;
- }
-
- Class getCls() {
- return this.cls;
- }
-
- java.lang.reflect.Type getType() {
- return this.type;
- }
-
- @Override
- public boolean equals(Object o) {
- if (o == null) {
- return false;
- }
-
- CacheKey other = (CacheKey) o;
-
- if (this.offset != other.offset) {
- return false;
- }
-
- if (this.cls == null) {
- if (other.cls != null) {
- return false;
- }
- } else if (!this.cls.equals(other.cls)) {
- return false;
- }
-
- if (this.type == null) {
- return other.type == null;
- }
- return this.type.equals(other.type);
- }
-
- @Override
- public int hashCode() {
- int result = offset;
- result = 31 * result + (cls == null ? 0 : cls.hashCode());
- result = 31 * result + (type == null ? 0 : type.hashCode());
- return result;
- }
+public record CacheKey(int offset, Class cls, java.lang.reflect.Type type) {
}
diff --git a/src/main/java/com/maxmind/db/Decoder.java b/src/main/java/com/maxmind/db/Decoder.java
index d8bca62e..fd14156e 100644
--- a/src/main/java/com/maxmind/db/Decoder.java
+++ b/src/main/java/com/maxmind/db/Decoder.java
@@ -73,7 +73,7 @@ T decode(int offset, Class cls) throws IOException {
}
private DecodedValue decode(CacheKey key) throws IOException {
- int offset = key.getOffset();
+ int offset = key.offset();
if (offset >= this.buffer.capacity()) {
throw new InvalidDatabaseException(
"The MaxMind DB file's data section contains bad data: "
@@ -81,8 +81,8 @@ private DecodedValue decode(CacheKey key) throws IOException {
}
this.buffer.position(offset);
- Class cls = key.getCls();
- return decode(cls, key.getType());
+ Class cls = key.cls();
+ return decode(cls, key.type());
}
private DecodedValue decode(Class cls, java.lang.reflect.Type genericType)
From 9a41e67d6df2d244e84a589a41dacc30574afa10 Mon Sep 17 00:00:00 2001
From: Gregory Oschwald
Date: Tue, 26 Aug 2025 14:40:00 -0700
Subject: [PATCH 4/9] Convert CachedConstructor to record
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Converts the internal CachedConstructor class to use Java 17 records,
reducing boilerplate and improving memory layout. Updates all getter
method calls to use record accessors.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude
---
.../com/maxmind/db/CachedConstructor.java | 39 +++----------------
src/main/java/com/maxmind/db/Decoder.java | 8 ++--
2 files changed, 10 insertions(+), 37 deletions(-)
diff --git a/src/main/java/com/maxmind/db/CachedConstructor.java b/src/main/java/com/maxmind/db/CachedConstructor.java
index 2b5b3160..c1c5f456 100644
--- a/src/main/java/com/maxmind/db/CachedConstructor.java
+++ b/src/main/java/com/maxmind/db/CachedConstructor.java
@@ -3,37 +3,10 @@
import java.lang.reflect.Constructor;
import java.util.Map;
-final class CachedConstructor {
- private final Constructor constructor;
- private final Class>[] parameterTypes;
- private final java.lang.reflect.Type[] parameterGenericTypes;
- private final Map parameterIndexes;
-
- CachedConstructor(
- Constructor constructor,
- Class>[] parameterTypes,
- java.lang.reflect.Type[] parameterGenericTypes,
- Map parameterIndexes
- ) {
- this.constructor = constructor;
- this.parameterTypes = parameterTypes;
- this.parameterGenericTypes = parameterGenericTypes;
- this.parameterIndexes = parameterIndexes;
- }
-
- Constructor getConstructor() {
- return this.constructor;
- }
-
- Class>[] getParameterTypes() {
- return this.parameterTypes;
- }
-
- java.lang.reflect.Type[] getParameterGenericTypes() {
- return this.parameterGenericTypes;
- }
-
- Map getParameterIndexes() {
- return this.parameterIndexes;
- }
+record CachedConstructor(
+ Constructor constructor,
+ Class>[] parameterTypes,
+ java.lang.reflect.Type[] parameterGenericTypes,
+ Map parameterIndexes
+) {
}
diff --git a/src/main/java/com/maxmind/db/Decoder.java b/src/main/java/com/maxmind/db/Decoder.java
index fd14156e..fa08cfc0 100644
--- a/src/main/java/com/maxmind/db/Decoder.java
+++ b/src/main/java/com/maxmind/db/Decoder.java
@@ -410,10 +410,10 @@ private Object decodeMapIntoObject(int size, Class cls)
)
);
} else {
- constructor = cachedConstructor.getConstructor();
- parameterTypes = cachedConstructor.getParameterTypes();
- parameterGenericTypes = cachedConstructor.getParameterGenericTypes();
- parameterIndexes = cachedConstructor.getParameterIndexes();
+ constructor = cachedConstructor.constructor();
+ parameterTypes = cachedConstructor.parameterTypes();
+ parameterGenericTypes = cachedConstructor.parameterGenericTypes();
+ parameterIndexes = cachedConstructor.parameterIndexes();
}
Object[] parameters = new Object[parameterTypes.length];
From 36e7354e46758d6b3d496a9fa40ae4368168e7d5 Mon Sep 17 00:00:00 2001
From: Gregory Oschwald
Date: Tue, 26 Aug 2025 14:40:58 -0700
Subject: [PATCH 5/9] Use pattern matching for instanceof
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Converts instanceof checks to use Java 17 pattern matching where
applicable, eliminating explicit casting and reducing boilerplate.
This makes the code more concise and type-safe.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude
---
src/main/java/com/maxmind/db/Decoder.java | 6 ++----
src/test/java/com/maxmind/db/DecoderTest.java | 4 ++--
2 files changed, 4 insertions(+), 6 deletions(-)
diff --git a/src/main/java/com/maxmind/db/Decoder.java b/src/main/java/com/maxmind/db/Decoder.java
index fa08cfc0..263b4310 100644
--- a/src/main/java/com/maxmind/db/Decoder.java
+++ b/src/main/java/com/maxmind/db/Decoder.java
@@ -158,8 +158,7 @@ private Object decodeByType(
return this.decodeMap(size, cls, genericType);
case ARRAY:
Class> elementClass = Object.class;
- if (genericType instanceof ParameterizedType) {
- ParameterizedType ptype = (ParameterizedType) genericType;
+ if (genericType instanceof ParameterizedType ptype) {
java.lang.reflect.Type[] actualTypes = ptype.getActualTypeArguments();
if (actualTypes.length == 1) {
elementClass = (Class>) actualTypes[0];
@@ -319,8 +318,7 @@ private Object decodeMap(
) throws IOException {
if (Map.class.isAssignableFrom(cls) || cls.equals(Object.class)) {
Class> valueClass = Object.class;
- if (genericType instanceof ParameterizedType) {
- ParameterizedType ptype = (ParameterizedType) genericType;
+ if (genericType instanceof ParameterizedType ptype) {
java.lang.reflect.Type[] actualTypes = ptype.getActualTypeArguments();
if (actualTypes.length == 2) {
Class> keyClass = (Class>) actualTypes[0];
diff --git a/src/test/java/com/maxmind/db/DecoderTest.java b/src/test/java/com/maxmind/db/DecoderTest.java
index f6e23569..92d6f3b1 100644
--- a/src/test/java/com/maxmind/db/DecoderTest.java
+++ b/src/test/java/com/maxmind/db/DecoderTest.java
@@ -465,8 +465,8 @@ private static void testTypeDecoding(Type type, Map tests)
String key = (String) keyObject;
Object value = expectMap.get(key);
- if (value instanceof Object[]) {
- assertArrayEquals((Object[]) value, (Object[]) got.get(key), desc);
+ if (value instanceof Object[] arrayValue) {
+ assertArrayEquals(arrayValue, (Object[]) got.get(key), desc);
} else {
assertEquals(value, got.get(key), desc);
}
From 163082b161c875f698b01a20412e601b8fb7ad09 Mon Sep 17 00:00:00 2001
From: Gregory Oschwald
Date: Tue, 26 Aug 2025 14:41:40 -0700
Subject: [PATCH 6/9] Convert switch statements to expressions
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Converts the decodeBoolean method switch statement to use Java 17
switch expressions, making the code more concise and eliminating
the need for break statements.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude
---
src/main/java/com/maxmind/db/Decoder.java | 47 ++++++++---------------
1 file changed, 17 insertions(+), 30 deletions(-)
diff --git a/src/main/java/com/maxmind/db/Decoder.java b/src/main/java/com/maxmind/db/Decoder.java
index 263b4310..81293267 100644
--- a/src/main/java/com/maxmind/db/Decoder.java
+++ b/src/main/java/com/maxmind/db/Decoder.java
@@ -120,16 +120,11 @@ private DecodedValue decode(Class cls, java.lang.reflect.Type genericType
int size = ctrlByte & 0x1f;
if (size >= 29) {
- switch (size) {
- case 29:
- size = 29 + (0xFF & buffer.get());
- break;
- case 30:
- size = 285 + decodeInteger(2);
- break;
- default:
- size = 65821 + decodeInteger(3);
- }
+ size = switch (size) {
+ case 29 -> 29 + (0xFF & buffer.get());
+ case 30 -> 285 + decodeInteger(2);
+ default -> 65821 + decodeInteger(3);
+ };
}
return new DecodedValue(this.decodeByType(type, size, cls, genericType));
@@ -259,16 +254,13 @@ private float decodeFloat(int size) throws InvalidDatabaseException {
private static boolean decodeBoolean(int size)
throws InvalidDatabaseException {
- switch (size) {
- case 0:
- return false;
- case 1:
- return true;
- default:
- throw new InvalidDatabaseException(
- "The MaxMind DB file's data section contains bad data: "
- + "invalid size of boolean.");
- }
+ return switch (size) {
+ case 0 -> false;
+ case 1 -> true;
+ default -> throw new InvalidDatabaseException(
+ "The MaxMind DB file's data section contains bad data: "
+ + "invalid size of boolean.");
+ };
}
private List decodeArray(
@@ -553,16 +545,11 @@ private CtrlData getCtrlData(int offset)
if (size >= 29) {
int bytesToRead = size - 28;
offset += bytesToRead;
- switch (size) {
- case 29:
- size = 29 + (0xFF & buffer.get());
- break;
- case 30:
- size = 285 + decodeInteger(2);
- break;
- default:
- size = 65821 + decodeInteger(3);
- }
+ size = switch (size) {
+ case 29 -> 29 + (0xFF & buffer.get());
+ case 30 -> 285 + decodeInteger(2);
+ default -> 65821 + decodeInteger(3);
+ };
}
return new CtrlData(type, ctrlByte, offset, size);
From 0c48b63244eccfc128ffde0e9232d77251c2fc33 Mon Sep 17 00:00:00 2001
From: Gregory Oschwald
Date: Tue, 26 Aug 2025 14:42:44 -0700
Subject: [PATCH 7/9] Update collections to use modern factory methods
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Replaces Arrays.asList() with List.of() for creating immutable lists
and updates empty array initialization to use modern syntax. This
improves performance and readability.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude
---
src/main/java/com/maxmind/db/Networks.java | 2 +-
src/test/java/com/maxmind/db/ReaderTest.java | 12 ++++++------
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/src/main/java/com/maxmind/db/Networks.java b/src/main/java/com/maxmind/db/Networks.java
index 93913c60..da6a0056 100644
--- a/src/main/java/com/maxmind/db/Networks.java
+++ b/src/main/java/com/maxmind/db/Networks.java
@@ -32,7 +32,7 @@ public final class Networks implements Iterator> {
*/
Networks(Reader reader, boolean includeAliasedNetworks, Class typeParameterClass)
throws ClosedDatabaseException {
- this(reader, includeAliasedNetworks, new NetworkNode[]{}, typeParameterClass);
+ this(reader, includeAliasedNetworks, new NetworkNode[0], typeParameterClass);
}
/**
diff --git a/src/test/java/com/maxmind/db/ReaderTest.java b/src/test/java/com/maxmind/db/ReaderTest.java
index 1925a9fb..702288a3 100644
--- a/src/test/java/com/maxmind/db/ReaderTest.java
+++ b/src/test/java/com/maxmind/db/ReaderTest.java
@@ -555,12 +555,12 @@ private void testDecodingTypesIntoModelObject(Reader reader, boolean booleanValu
assertEquals("unicode! ☯ - ♫", model.utf8StringField);
- List expectedArray = new ArrayList<>(Arrays.asList(
+ List expectedArray = new ArrayList<>(List.of(
(long) 1, (long) 2, (long) 3
));
assertEquals(expectedArray, model.arrayField);
- List expectedArray2 = new ArrayList<>(Arrays.asList(
+ List expectedArray2 = new ArrayList<>(List.of(
(long) 7, (long) 8, (long) 9
));
assertEquals(expectedArray2, model.mapField.mapXField.arrayXField);
@@ -675,12 +675,12 @@ private void testDecodingTypesIntoModelObjectBoxed(Reader reader, boolean boolea
assertEquals("unicode! ☯ - ♫", model.utf8StringField);
- List expectedArray = new ArrayList<>(Arrays.asList(
+ List expectedArray = new ArrayList<>(List.of(
(long) 1, (long) 2, (long) 3
));
assertEquals(expectedArray, model.arrayField);
- List expectedArray2 = new ArrayList<>(Arrays.asList(
+ List expectedArray2 = new ArrayList<>(List.of(
(long) 7, (long) 8, (long) 9
));
assertEquals(expectedArray2, model.mapField.mapXField.arrayXField);
@@ -785,7 +785,7 @@ private void testDecodingTypesIntoModelWithList(Reader reader)
throws IOException {
TestModelList model = reader.get(InetAddress.getByName("::1.1.1.0"), TestModelList.class);
- assertEquals(Arrays.asList((long) 1, (long) 2, (long) 3), model.arrayField);
+ assertEquals(List.of((long) 1, (long) 2, (long) 3), model.arrayField);
}
static class TestModelList {
@@ -1235,7 +1235,7 @@ private void testMetadata(Reader reader, int ipVersion, long recordSize) {
assertEquals(ipVersion, metadata.getIpVersion());
assertEquals("Test", metadata.getDatabaseType());
- List languages = new ArrayList<>(Arrays.asList("en", "zh"));
+ List languages = new ArrayList<>(List.of("en", "zh"));
assertEquals(languages, metadata.getLanguages());
From 7f30a90c2a4fb1c8a19036c64c7c769ce9ce22d5 Mon Sep 17 00:00:00 2001
From: Gregory Oschwald
Date: Tue, 26 Aug 2025 14:56:02 -0700
Subject: [PATCH 8/9] Remove whitespace at line ends
---
README.md | 4 ++--
src/main/java/com/maxmind/db/Decoder.java | 2 +-
.../java/com/maxmind/db/NetworksIterationException.java | 4 ++--
src/main/java/com/maxmind/db/NoCache.java | 2 +-
src/main/java/com/maxmind/db/NodeCache.java | 2 +-
src/test/java/com/maxmind/db/ReaderTest.java | 6 +++---
6 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/README.md b/README.md
index b4744786..37f471c8 100644
--- a/README.md
+++ b/README.md
@@ -120,8 +120,8 @@ public class Lookup {
}
```
-You can also use the reader object to iterate over the database.
-The `reader.networks()` and `reader.networksWithin()` methods can
+You can also use the reader object to iterate over the database.
+The `reader.networks()` and `reader.networksWithin()` methods can
be used for this purpose.
```java
diff --git a/src/main/java/com/maxmind/db/Decoder.java b/src/main/java/com/maxmind/db/Decoder.java
index 81293267..8ccd3aa0 100644
--- a/src/main/java/com/maxmind/db/Decoder.java
+++ b/src/main/java/com/maxmind/db/Decoder.java
@@ -140,7 +140,7 @@ DecodedValue decodePointer(long pointer, Class> cls, java.lang.reflect.Type ge
buffer.position(position);
return o;
- }
+ }
private Object decodeByType(
Type type,
diff --git a/src/main/java/com/maxmind/db/NetworksIterationException.java b/src/main/java/com/maxmind/db/NetworksIterationException.java
index ad24a88c..5f13c9d8 100644
--- a/src/main/java/com/maxmind/db/NetworksIterationException.java
+++ b/src/main/java/com/maxmind/db/NetworksIterationException.java
@@ -5,14 +5,14 @@
* This class represents an error encountered while iterating over the networks.
* The most likely causes are corrupt data in the database, or a bug in the reader code.
*
- *
+ *
* This exception extends RuntimeException because it is thrown by the iterator
* methods in {@link Networks}.
*
*
* @see Networks
*/
-public class NetworksIterationException extends RuntimeException {
+public class NetworksIterationException extends RuntimeException {
NetworksIterationException(String message) {
super(message);
}
diff --git a/src/main/java/com/maxmind/db/NoCache.java b/src/main/java/com/maxmind/db/NoCache.java
index f169e3b3..59ad6cd2 100644
--- a/src/main/java/com/maxmind/db/NoCache.java
+++ b/src/main/java/com/maxmind/db/NoCache.java
@@ -16,7 +16,7 @@ private NoCache() {
public DecodedValue get(CacheKey key, Loader loader) throws IOException {
return loader.load(key);
}
-
+
/**
* @return the singleton instance of the NoCache class
*/
diff --git a/src/main/java/com/maxmind/db/NodeCache.java b/src/main/java/com/maxmind/db/NodeCache.java
index d7235813..4e9102ad 100644
--- a/src/main/java/com/maxmind/db/NodeCache.java
+++ b/src/main/java/com/maxmind/db/NodeCache.java
@@ -23,7 +23,7 @@ interface Loader {
/**
* This method returns the value for the key. If the key is not in the cache
- * then the loader is called to load the value.
+ * then the loader is called to load the value.
*
* @param key
* the key to look up
diff --git a/src/test/java/com/maxmind/db/ReaderTest.java b/src/test/java/com/maxmind/db/ReaderTest.java
index 702288a3..d2ff0904 100644
--- a/src/test/java/com/maxmind/db/ReaderTest.java
+++ b/src/test/java/com/maxmind/db/ReaderTest.java
@@ -172,7 +172,7 @@ public networkTest(String network, int prefix,String database, String[] expecte
}
),
new networkTest(
- "255.255.255.0",
+ "255.255.255.0",
24,
"ipv4",
new String[]{}
@@ -238,7 +238,7 @@ public networkTest(String network, int prefix,String database, String[] expecte
"1.1.1.4/30",
"1.1.1.8/29",
"1.1.1.16/28",
- "1.1.1.32/32",
+ "1.1.1.32/32",
}
),
new networkTest(
@@ -251,7 +251,7 @@ public networkTest(String network, int prefix,String database, String[] expecte
"1.1.1.4/30",
"1.1.1.8/29",
"1.1.1.16/28",
- "1.1.1.32/32",
+ "1.1.1.32/32",
},
true
),
From bd42688a9cb2f0e8055a872b47b4fb22dd7aeb10 Mon Sep 17 00:00:00 2001
From: Gregory Oschwald
Date: Wed, 27 Aug 2025 07:53:49 -0700
Subject: [PATCH 9/9] Fix raw uses of parameterized classes
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Replace raw types with properly parameterized generics:
- ConcurrentHashMap → ConcurrentHashMap, CachedConstructor>>
- CacheKey → CacheKey>
- Map t → Map t
Add type-safe helper method getCachedConstructor() to encapsulate necessary
unchecked cast with clear documentation of safety invariant.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude
---
sample/Benchmark.java | 2 +-
src/main/java/com/maxmind/db/CHMCache.java | 4 ++--
src/main/java/com/maxmind/db/Decoder.java | 17 ++++++++++++-----
src/main/java/com/maxmind/db/NoCache.java | 2 +-
src/main/java/com/maxmind/db/NodeCache.java | 4 ++--
src/main/java/com/maxmind/db/Reader.java | 2 +-
6 files changed, 19 insertions(+), 12 deletions(-)
diff --git a/sample/Benchmark.java b/sample/Benchmark.java
index 3d73011b..b0f8fd2e 100644
--- a/sample/Benchmark.java
+++ b/sample/Benchmark.java
@@ -45,7 +45,7 @@ private static void bench(Reader r, int count, int seed) throws IOException {
for (int i = 0; i < count; i++) {
random.nextBytes(address);
InetAddress ip = InetAddress.getByAddress(address);
- Map t = r.get(ip, Map.class);
+ Map t = r.get(ip, Map.class);
if (TRACE) {
if (i % 50000 == 0) {
System.out.println(i + " " + ip);
diff --git a/src/main/java/com/maxmind/db/CHMCache.java b/src/main/java/com/maxmind/db/CHMCache.java
index f9f63292..0b22d4cc 100644
--- a/src/main/java/com/maxmind/db/CHMCache.java
+++ b/src/main/java/com/maxmind/db/CHMCache.java
@@ -13,7 +13,7 @@ public class CHMCache implements NodeCache {
private static final int DEFAULT_CAPACITY = 4096;
private final int capacity;
- private final ConcurrentHashMap cache;
+ private final ConcurrentHashMap, DecodedValue> cache;
private boolean cacheFull = false;
/**
@@ -36,7 +36,7 @@ public CHMCache(int capacity) {
}
@Override
- public DecodedValue get(CacheKey key, Loader loader) throws IOException {
+ public DecodedValue get(CacheKey> key, Loader loader) throws IOException {
DecodedValue value = cache.get(key);
if (value == null) {
value = loader.load(key);
diff --git a/src/main/java/com/maxmind/db/Decoder.java b/src/main/java/com/maxmind/db/Decoder.java
index 8ccd3aa0..c1c7446d 100644
--- a/src/main/java/com/maxmind/db/Decoder.java
+++ b/src/main/java/com/maxmind/db/Decoder.java
@@ -36,7 +36,7 @@ class Decoder {
private final ByteBuffer buffer;
- private final ConcurrentHashMap constructors;
+ private final ConcurrentHashMap, CachedConstructor>> constructors;
Decoder(NodeCache cache, ByteBuffer buffer, long pointerBase) {
this(
@@ -51,7 +51,7 @@ class Decoder {
NodeCache cache,
ByteBuffer buffer,
long pointerBase,
- ConcurrentHashMap constructors
+ ConcurrentHashMap, CachedConstructor>> constructors
) {
this.cache = cache;
this.pointerBase = pointerBase;
@@ -135,7 +135,7 @@ DecodedValue decodePointer(long pointer, Class> cls, java.lang.reflect.Type ge
int targetOffset = (int) pointer;
int position = buffer.position();
- CacheKey key = new CacheKey(targetOffset, cls, genericType);
+ CacheKey> key = new CacheKey<>(targetOffset, cls, genericType);
DecodedValue o = cache.get(key, cacheLoader);
buffer.position(position);
@@ -371,7 +371,7 @@ private Map decodeMapIntoMap(
private Object decodeMapIntoObject(int size, Class cls)
throws IOException {
- CachedConstructor cachedConstructor = this.constructors.get(cls);
+ CachedConstructor cachedConstructor = getCachedConstructor(cls);
Constructor constructor;
Class>[] parameterTypes;
java.lang.reflect.Type[] parameterGenericTypes;
@@ -392,7 +392,7 @@ private Object decodeMapIntoObject(int size, Class cls)
this.constructors.put(
cls,
- new CachedConstructor(
+ new CachedConstructor<>(
constructor,
parameterTypes,
parameterGenericTypes,
@@ -445,6 +445,13 @@ private Object decodeMapIntoObject(int size, Class cls)
}
}
+ private CachedConstructor getCachedConstructor(Class cls) {
+ // This cast is safe because we only put CachedConstructor for Class as the key
+ @SuppressWarnings("unchecked")
+ CachedConstructor result = (CachedConstructor) this.constructors.get(cls);
+ return result;
+ }
+
private static Constructor findConstructor(Class cls)
throws ConstructorNotFoundException {
Constructor>[] constructors = cls.getConstructors();
diff --git a/src/main/java/com/maxmind/db/NoCache.java b/src/main/java/com/maxmind/db/NoCache.java
index 59ad6cd2..43cda527 100644
--- a/src/main/java/com/maxmind/db/NoCache.java
+++ b/src/main/java/com/maxmind/db/NoCache.java
@@ -13,7 +13,7 @@ private NoCache() {
}
@Override
- public DecodedValue get(CacheKey key, Loader loader) throws IOException {
+ public DecodedValue get(CacheKey> key, Loader loader) throws IOException {
return loader.load(key);
}
diff --git a/src/main/java/com/maxmind/db/NodeCache.java b/src/main/java/com/maxmind/db/NodeCache.java
index 4e9102ad..4dd9175c 100644
--- a/src/main/java/com/maxmind/db/NodeCache.java
+++ b/src/main/java/com/maxmind/db/NodeCache.java
@@ -18,7 +18,7 @@ interface Loader {
* @throws IOException
* if there is an error loading the value
*/
- DecodedValue load(CacheKey key) throws IOException;
+ DecodedValue load(CacheKey> key) throws IOException;
}
/**
@@ -33,6 +33,6 @@ interface Loader {
* @throws IOException
* if there is an error loading the value
*/
- DecodedValue get(CacheKey key, Loader loader) throws IOException;
+ DecodedValue get(CacheKey> key, Loader loader) throws IOException;
}
diff --git a/src/main/java/com/maxmind/db/Reader.java b/src/main/java/com/maxmind/db/Reader.java
index 15609c95..c9e41591 100644
--- a/src/main/java/com/maxmind/db/Reader.java
+++ b/src/main/java/com/maxmind/db/Reader.java
@@ -26,7 +26,7 @@ public final class Reader implements Closeable {
private final Metadata metadata;
private final AtomicReference bufferHolderReference;
private final NodeCache cache;
- private final ConcurrentHashMap constructors;
+ private final ConcurrentHashMap, CachedConstructor>> constructors;
/**
* The file mode to use when opening a MaxMind DB.