Backend Development 8 min read

Why Property Copy Tools in Java Are Risky: Performance Issues and Safer Alternatives

The article explains why using automatic property copy utilities in Java can lead to performance degradation, hidden type‑conversion bugs, and runtime errors, and demonstrates safer approaches such as manually written converters, IDE‑generated code, and MapStruct with concrete examples and code snippets.

Java Architect Essentials
Java Architect Essentials
Java Architect Essentials
Why Property Copy Tools in Java Are Risky: Performance Issues and Safer Alternatives

In a previous column the author warned against using generic property‑copy tools and suggested defining explicit conversion classes or using IDE plugins to generate getters and setters automatically.

Main reasons for the recommendation:

Some property‑copy utilities have poor performance.

Several tools contain bugs.

Using them can hide subtle type‑conversion hazards that only appear at runtime.

Example with Spring BeanUtils

A real case showed that org.springframework.beans.BeanUtils.copyProperties copied a List<Integer> into a field declared as List<String> , causing a ClassCastException when iterating.

import lombok.Data;

import java.util.List;

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

@Data
public class B {
    private String name;
    private List
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);
        for (String each : second.getIds()) {
            // Type conversion exception
            System.out.println(each);
        }
    }
}

Running the code throws a type‑conversion exception because the generic type information is erased at runtime.

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 beanCopier = BeanCopier.create(A.class, B.class, false);
        beanCopier.copy(first, second, null);
        for (String each : second.getIds()) {
            // Type conversion exception
            System.out.println(each);
        }
    }
}

The same problem appears at runtime when CGLIB copies the properties.

Using MapStruct

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

@Mapper
public interface Converter {
    Converter INSTANCE = Mappers.getMapper(Converter.class);
    B aToB(A a);
}
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()) {
            // Works correctly
            System.out.println(each);
        }
    }
}

MapStruct generates a converter that automatically transforms List<Integer> to List<String> , but this hidden conversion can mask type mismatches and introduce side effects.

If mismatched fields are added (e.g., String number in A and Long number in B), MapStruct will throw a NumberFormatException when the source value is non‑numeric, whereas CGLIB would simply leave the target field null.

Conclusion

Because Java generics are erased after compilation, both List<Integer> and List<String> become raw List objects at runtime, allowing unsafe assignments that only surface during execution. Tools like MapStruct can read generic types at compile time and perform conversions, but they may also hide errors.

Therefore, it is advisable to avoid generic property‑copy utilities when possible, prefer explicit conversion classes (which IDE plugins can generate quickly), and rely on direct getter/setter calls for better compile‑time safety and performance.

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

login 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.