Why Property‑Copy Tools Fail: Performance Pitfalls and Hidden Type Mismatches in Java

The article analyzes common Java property‑copy utilities, showing how tools like Spring BeanUtils, CGLIB BeanCopier and MapStruct can suffer from poor performance and runtime type‑conversion errors, and recommends using explicit converters generated by IDE plugins to avoid hidden bugs.

Java Architect Essentials
Java Architect Essentials
Java Architect Essentials
Why Property‑Copy Tools Fail: Performance Pitfalls and Hidden Type Mismatches in Java

Background

Using generic property‑copy utilities (e.g., Spring BeanUtils, CGLIB BeanCopier) can hide type mismatches because Java generics are erased at runtime. The compiler allows copying between objects with incompatible generic fields, leading to ClassCastException or other runtime errors.

Problems with Generic Property‑Copy Tools

Performance may be sub‑optimal.

Some implementations contain bugs.

Type‑safety is not guaranteed; mismatched generic types cause runtime failures.

Example with Spring BeanUtils

import lombok.Data;
import java.util.List;

@Data
public class A {
    private String name;
    private List<Integer> ids;
}

@Data
public class B {
    private String name;
    private List<String> ids;
}
import org.springframework.beans.BeanUtils;
import java.util.Arrays;

public class BeanUtilDemo {
    public static void main(String[] args) {
        A first = new A();
        first.setName("demo");
        first.setIds(Arrays.asList(1, 2, 3));

        B second = new B();
        BeanUtils.copyProperties(first, second);
        // Runtime ClassCastException when iterating over second.getIds()
        for (String each : second.getIds()) {
            System.out.println(each);
        }
    }
}

The copy copies the raw List reference; the generic type List<String> in B actually holds Integer elements, causing a ClassCastException.

BeanUtils type mismatch
BeanUtils type mismatch

Example with CGLIB BeanCopier

import org.easymock.cglib.beans.BeanCopier;
import java.util.Arrays;

public class BeanUtilDemo {
    public static void main(String[] args) {
        A first = new A();
        first.setName("demo");
        first.setIds(Arrays.asList(1, 2, 3));

        B second = new B();
        BeanCopier copier = BeanCopier.create(A.class, B.class, false);
        copier.copy(first, second, null);
        // Same runtime exception as above
        for (String each : second.getIds()) {
            System.out.println(each);
        }
    }
}

Using MapStruct

import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;

@Mapper
public interface Converter {
    Converter INSTANCE = Mappers.getMapper(Converter.class);
    B aToB(A source);
}
import java.util.Arrays;

public class BeanUtilDemo {
    public static void main(String[] args) {
        A first = new A();
        first.setName("demo");
        first.setIds(Arrays.asList(1, 2, 3));

        B second = Converter.INSTANCE.aToB(first);
        for (String each : second.getIds()) {
            System.out.println(each);
        }
    }
}

MapStruct generates a mapper that safely converts List<Integer> to List<String> at compile time.

import java.util.ArrayList;
import java.util.List;
import javax.annotation.Generated;
import org.springframework.stereotype.Component;

@Generated(value = "org.mapstruct.ap.MappingProcessor",
          comments = "version: 1.3.1.Final, compiler: javac, environment: Java 1.8.0_202")
@Component
public class ConverterImpl implements Converter {
    @Override
    public B aToB(A source) {
        if (source == null) {
            return null;
        }
        B target = new B();
        target.setName(source.getName());
        target.setIds(integerListToStringList(source.getIds()));
        return target;
    }

    protected List<String> integerListToStringList(List<Integer> list) {
        if (list == null) {
            return null;
        }
        List<String> result = new ArrayList<>(list.size());
        for (Integer i : list) {
            result.add(String.valueOf(i));
        }
        return result;
    }
}

Although convenient, automatic conversion can mask unintended type changes.

Custom Converter Example

public final class A2BConverter {
    public static B from(A source) {
        B target = new B();
        target.setName(source.getName());
        target.setIds(source.getIds()); // compile‑time type check
        return target;
    }
}

Explicit conversion forces the compiler to verify compatible property types, preventing runtime mismatches.

Custom converter compilation error
Custom converter compilation error

Conclusion

Java generics are erased after compilation, so List<Integer> and List<String> are both represented as raw List at runtime. Property‑copy tools that rely on reflection cannot detect generic mismatches, leading to runtime ClassCastException. MapStruct’s annotation processor reads generic information at compile time and generates type‑safe mappers, but developers must still ensure target types are correct because automatic conversion may hide logic errors. The safest approach is to define explicit converter classes (or let IDE plugins generate them) so that type incompatibilities are caught during compilation and performance overhead is minimized.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaperformanceBeanUtilsmapstructtype conversionproperty copy
Java Architect Essentials
Written by

Java Architect Essentials

Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow together.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.