Affects: spring-beans-5.1.2.RELEASE
class XA{}
class XB{}
class A{
List<XA> a = new ArrayList<>();
private String b;
private String c;
public List<XA> getA() {
return a;
}
public void setA(List<XA> a) {
this.a = a;
}
public String getB() {
return b;
}
public void setB(String b) {
this.b = b;
}
public String getC() {
return c;
}
public void setC(String c) {
this.c = c;
}
}
class B {
List<XB> a = new ArrayList<>();
private String b;
private String c;
public List<XB> getA() {
return a;
}
public void setA(List<XB> a) {
this.a = a;
}
public String getB() {
return b;
}
public void setB(String b) {
this.b = b;
}
public String getC() {
return c;
}
public void setC(String c) {
this.c = c;
}
}
public class TestBeanUtils{
public static void main(String[] args) {
A a = new A();
a.getA().add(new XA());
B b= new B();
BeanUtils.copyProperties(a,b);
List<XB> a1 = b.getA();
for (XB xb : a1) {
System.out.println("AAA");
}
List<XA> a2 = a.getA();
for (XA xb : a2) {
System.out.println("AAA");
}
}
}
run the main method , then there will throw a java.lang.ClassCastException;
Comment From: iProcess
expect solution
Comment From: sbrannen
Thank you for raising the issue.
I have distilled the behavior via the following test.
public class BeanUtilsTests {
@Test
void test() {
A a = new A();
a.getList().add(42);
B b = new B();
BeanUtils.copyProperties(a, b);
assertThat(a.getList()).containsOnly(42);
b.getList().forEach(n -> assertThat(n).isInstanceOf(Long.class));
assertThat(b.getList()).isEmpty();
}
}
class A {
private List<Integer> list = new ArrayList<>();
public List<Integer> getList() {
return list;
}
public void setList(List<Integer> list) {
this.list = list;
}
}
class B {
private List<Long> list = new ArrayList<>();
public List<Long> getList() {
return list;
}
public void setList(List<Long> list) {
this.list = list;
}
}
The reason the test fails with a ClassCastException is that the generic type information is ignored in BeanUtils.copyProperties(Object, Object, Class<?>, String...) since it performs a comparison between the return type of the read method in the source and parameter type of the write method in the target, both of which are java.util.List.
Note, however, that the generic type information is available in the java.lang.reflect.Method instances used. Thus, Spring could potentially honor the generic type information when copying properties; however, Spring currently ignores the generic type information.
Comment From: kpkunal1406
@sbrannen, See Pull request #24281.
Handled Test Case is as below.
@Test
void testCopiedParametersType() {
A a = new A();
B b = new B();
a.getList().add(42);
a.getList2().add(34.2F);
BeanUtils.copyProperties(a, b);
assertThat(b.getList()).containsOnly(42L);
b.getList().forEach(n -> assertThat(n).isInstanceOf(Long.class));
assertThat(b.getList()).isNotEmpty();
b.getList2().forEach(n -> assertThat(n).isInstanceOf(Integer.class));
assertThat(b.getList2()).isNotEmpty();
}
class A {
private List<Integer> list = new ArrayList<>();
private List<Float> list2 = new ArrayList<>();
public List<Integer> getList() {
return list;
}
public void setList(List<Integer> list) {
this.list = list;
}
public List<Float> getList2() {
return list2;
}
public void setList2(List<Float> list2) {
this.list2 = list2;
}
}
class B {
private List<Long> list = new ArrayList<>();
private List<Integer> list2 = new ArrayList<>();
public List<Long> getList() {
return list;
}
public void setList(List<Long> list) {
this.list = list;
}
public List<Integer> getList2() {
return list2;
}
public void setList2(List<Integer> list2) {
this.list2 = list2;
}
}
I have used convertIfNecessary method of SimpleTypeConverter class. This does all standard datatype casting and conversions and copies into target object's properties. If there is any custom object like XA or XB then it throws casting exception. It has not handled knowingly otherwise it just assigns target object's property with source's property's datatype (If names are same). No meaning of that
Comment From: kpkunal1406
@sbrannen ,
please check #24281 with required new implementation. and let me know if any change is required.
Comment From: sbrannen
Superseded by #24281