为何Java ArrayList无法转换为继承List的自定义接口?
为什么ArrayList实例无法转换为继承List的自定义接口?
问题场景
我定义了一个继承List的泛型接口,用于3处业务场景并配套构建器:
public interface RefInItemsListInput <T extends Object> extends List<T> {}
另有类RefInItems1的validate方法接收该接口类型的参数:
public static class RefInItems1 extends JsonSchema { public static RefInItemsList validate(RefInItemsListInput arg, SchemaConfiguration configuration) throws ValidationException { return JsonSchema.validateList(RefInItems1.class, arg, configuration); } }
在JUnit测试中,我尝试将Arrays.asList返回的实例强制转换为RefInItemsListInput:
@Test public void testPropertyNamedRefValidPasses() { // property named $ref valid RefInItems.RefInItems1.validate( (RefInItems.RefInItemsListInput) Arrays.asList( MapMaker.makeMap( new AbstractMap.SimpleEntry<>( "$ref", "a" ) ) ), configuration ); }
运行测试时抛出ClassCastException错误:
java.lang.ClassCastException: class java.util.Arrays$ArrayList cannot be cast to class org.openapijsonschematools.client.components.schemas.RefInItems$RefInItemsListInput (java.util.Arrays$ArrayList is in module java.base of loader 'bootstrap'; org.openapijsonschematools.client.components.schemas.RefInItems$RefInItemsListInput is in unnamed module of loader 'app') at org.openapijsonschematools.client.components.schemas.RefInItemsTest.testPropertyNamedRefValidPasses(RefInItemsTest.java:23) ...
原因分析
这是Java类型转换的基础规则导致的:
- 一个对象只能被转换成它实际实现的接口,或者它的父类/父接口类型。
Arrays.asList()返回的内部类java.util.Arrays$ArrayList只实现了JDK自带的List、RandomAccess等接口,完全没有实现你自定义的RefInItemsListInput接口。哪怕你的接口继承了List,也只是让它成为List的子接口,并不会让所有List实现类自动兼容这个子接口。
解决方案
方案1:实现包装类(适配器模式)
写一个实现RefInItemsListInput的包装类,把普通List实例包裹进去,转发所有方法调用:
public class RefInItemsListInputWrapper<T> implements RefInItemsListInput<T> { private final List<T> delegate; public RefInItemsListInputWrapper(List<T> delegate) { this.delegate = delegate; } // 实现List接口的所有方法,直接委托给内部的delegate @Override public int size() { return delegate.size(); } @Override public boolean isEmpty() { return delegate.isEmpty(); } @Override public boolean contains(Object o) { return delegate.contains(o); } // 其余List接口方法同理实现... }
测试代码修改为:
RefInItems.RefInItems1.validate( new RefInItemsListInputWrapper<>( Arrays.asList( MapMaker.makeMap( new AbstractMap.SimpleEntry<>("$ref", "a") ) ) ), configuration );
方案2:调整方法参数类型(如果业务允许)
如果RefInItemsListInput只是作为标记接口,没有额外的方法定义,可以直接把validate方法的参数类型改为List<T>,这样就不需要强转,直接传入Arrays.asList的结果即可:
public static RefInItemsList validate(List arg, SchemaConfiguration configuration) throws ValidationException { return JsonSchema.validateList(RefInItems1.class, arg, configuration); }
内容的提问来源于stack exchange,提问作者spacether
相关产品推荐
相关产品推荐

