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 <T> void unitMapConvert(List<T> list, Map<String, UnitConvertType> 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 <T> void unitAnnotateConvert(List<T> 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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
