BeanUtils.copyProperties 导致的 ClassCastException

static class Foo {
        private List<String> list;

        public List<String> getList() {
            return list;
        }

        public void setList(List<String> list) {
            this.list = list;
        }
    }

    static class Bar {
        private List<Integer> list;

        public List<Integer> getList() {
            return list;
        }

        public void setList(List<Integer> list) {
            this.list = list;
        }
    }
    
    public static void main(String[] args) {
        Foo foo = new Foo();
        List<String> list = new ArrayList<>();
        list.add("asdasd");
        list.add("asdasd");
        foo.setList(list);
        Bar bar = new Bar();
        // 重点
        BeanUtils.copyProperties(foo, bar);

        bar.getList().forEach(item -> {
            System.out.println(item);
        });
    }

这个一看是不是没有任何问题,java编译器也过的去,运行会不会报错呢?

答案就是会报错!!!

Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
	at java.util.ArrayList.forEach(ArrayList.java:1249)
	at com.wuhulala.mybatis.BeanUtilsTest.main(BeanUtilsTest.java:49)

为啥会报错呢???

先看一下源码

private static void copyProperties(Object source, Object target, Class<?> editable, String... ignoreProperties)
			throws BeansException {

		Assert.notNull(source, "Source must not be null");
		Assert.notNull(target, "Target must not be null");

		Class<?> actualEditable = target.getClass();
		if (editable != null) {
			if (!editable.isInstance(target)) {
				throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
						"] not assignable to Editable class [" + editable.getName() + "]");
			}
			actualEditable = editable;
		}
		PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
		List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);

		for (PropertyDescriptor targetPd : targetPds) {
			Method writeMethod = targetPd.getWriteMethod();
			if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
				PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
				if (sourcePd != null) {
					Method readMethod = sourcePd.getReadMethod();
					// 重点就是这里里面使用的ClassUtils.isAssignable里面
					if (readMethod != null &&
							ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
						try {
							if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
								readMethod.setAccessible(true);
							}
							Object value = readMethod.invoke(source);
							if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
								writeMethod.setAccessible(true);
							}
							writeMethod.invoke(target, value);
						}
						catch (Throwable ex) {
							throw new FatalBeanException(
									"Could not copy property '" + targetPd.getName() + "' from source to target", ex);
						}
					}
				}
			}
		}
	}

重点就是这里里面使用的ClassUtils.isAssignable,我们来看一下我们的bar的写方法的参数类型和foo读方法的返回类型
在这里插入图片描述
可以看到都是java.util.List。那么这个也解释的通了。

其实这里就是因为Java的泛型擦除原理,所以在copyProperties方法认为他们两个list都是一致的,但是在实际应用的时候,却是使用的Integer,所以会导致类型不一致问题。

那么如何可以解决这个问题呢?

  1. 不解决,在应用的时候注意一点
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章