Automatic Unit Conversion in Java Using Maps and Custom Annotations
This article explains how to automate unit conversions for Java DTOs by first using a Map to mark fields and an enum for conversion types, then enhancing the solution with a custom annotation and reflection to make the process more reusable and maintainable.
When retrieving statistical data from databases or APIs, the units often do not match business requirements, requiring conversions such as cents to yuan, percentages, per‑mil, scaling to ten‑thousands, or rounding to two decimals.
Traditional approaches involve manually iterating over a list of DTOs and repeatedly calling getters and setters, which is tedious and error‑prone.
Solution ① – Basic encapsulation using a Map to mark fields that need conversion. An enum UnitConvertType defines conversion types (R, B, PERCENTAGE, PERMIL). The utility class UnitConvertUtil uses reflection to read each field, checks the map, applies the appropriate arithmetic, and writes the result back.
public enum UnitConvertType {
R, // precision, keep 2 decimal places
B, // convert to ten‑thousands (万元)
PERCENTAGE, // convert to percent
PERMIL // convert to per‑mil
} public class UnitConvertUtil {
public static
void unitMapConvert(List
list, Map
propertyMap) {
for (T t : list) {
for (Field field : t.getClass().getDeclaredFields()) {
if (propertyMap.containsKey(field.getName())) {
field.setAccessible(true);
Object value = field.get(t);
UnitConvertType type = propertyMap.get(field.getName());
if (value != null) {
BigDecimal bd = (BigDecimal) value;
switch (type) {
case PERCENTAGE:
field.set(t, bd.multiply(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP));
break;
case PERMIL:
field.set(t, bd.multiply(new BigDecimal(1000)).setScale(2, BigDecimal.ROUND_HALF_UP));
break;
case B:
field.set(t, bd.divide(new BigDecimal(10000)).setScale(2, BigDecimal.ROUND_HALF_UP));
break;
case R:
field.set(t, bd.setScale(2, BigDecimal.ROUND_HALF_UP));
break;
}
}
}
}
}
}
}Solution ② – Further encapsulation with a custom annotation @JcBigDecConvert . Fields in a DTO (e.g., MyYearSumReportDTO ) are annotated with the desired UnitConvertType . A reflective method unitAnnotateConvert scans annotations and performs the same conversions, eliminating the need for an external map.
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface JcBigDecConvert {
UnitConvertType name();
} @Data
public class MyYearSumReportDTO implements Serializable {
@JcBigDecConvert(name = UnitConvertType.B)
private BigDecimal payTotalAmount;
@JcBigDecConvert(name = UnitConvertType.PERCENTAGE)
private BigDecimal jcAmountPercentage;
@JcBigDecConvert(name = UnitConvertType.PERMIL)
private BigDecimal jcCountPermillage;
@JcBigDecConvert(name = UnitConvertType.R)
private BigDecimal length;
@JcBigDecConvert(name = UnitConvertType.R)
private BigDecimal width;
} public static
void unitAnnotateConvert(List
list) {
for (T t : list) {
for (Field field : t.getClass().getDeclaredFields()) {
JcBigDecConvert ann = field.getAnnotation(JcBigDecConvert.class);
if (ann == null) continue;
field.setAccessible(true);
Object value = field.get(t);
if (value == null) continue;
BigDecimal bd = (BigDecimal) value;
switch (ann.name()) {
case PERCENTAGE:
field.set(t, bd.multiply(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP));
break;
case PERMIL:
field.set(t, bd.multiply(new BigDecimal(1000)).setScale(2, BigDecimal.ROUND_HALF_UP));
break;
case B:
field.set(t, bd.divide(new BigDecimal(10000)).setScale(2, BigDecimal.ROUND_HALF_UP));
break;
case R:
field.set(t, bd.setScale(2, BigDecimal.ROUND_HALF_UP));
break;
}
}
}
}Both approaches are demonstrated with sample data and console output showing the transformed values, proving that the conversion logic can be easily extended or customized.
The article encourages readers to adapt the conversion logic to their own projects and view the code as a reusable utility.
Architect's Guide
Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.