使⽤Lombok@Singular注解需要注意的BUG
先看下⾯⼀个 java 类, 这是在业务代码中的⼀段真实代码,做了部分简化。
@Getter
@Builder
@NoArgsConstructor
public class PatientQuery {
@Singular
private List<Long> patientIds = wArrayList();
}
⾸先我们先分析下上⾯这段代码存在的问题:
**第⼀:**添加了 Lombok 的 @Builder 注解,但是 patientIds 属性有默认值,却没有添加 @Builder.Default 注解,这样使⽤ builder 模式构建这个对象时会导致 属性 patientIds 是个 null。
第⼆: 当我们添加了 @Builder.Default注解后,编译器会报错, @Builder.Default和@Singular 不能⼀块使⽤。
所以上⾯的代码, 如果想⽤ @Singluar 注解要写成下⾯这样才对:
@Getter
@Builder
@NoArgsConstructor
public class PatientQuery {
@Singular
private List<Long> patientIds;
}
如果想⽤ @Builder.Default注解给 patientIds 属性添加默认值,要写成下⾯这样:
@Getter
@Builder
@NoArgsConstructor
public class PatientQuery {
@Builder.Default
private List<Long> patientIds = wArrayList();
}
当我们使⽤ @Singular的时候,可能还会犯下⾯的错误:
public static void main(String[] args){
PatientQuery query = PatientQuery.builder().build();
}
⼤家可以先思考⼀下,上⾯的代码会出现什么错误。
上⾯的代码运⾏时会报这样的异常:
java.lang.UnsupportedOperationException: null
at java.base/java.util.AbstractList.add(AbstractList.java:153)
异常信息指⽰我们,List#add()是个不⽀持的操作,查看 AbstractList.add()⽅法源码:
public void add(int index, E element){
throw new UnsupportedOperationException();
}
默认不⽀持 add 操作,需要⼦类去实现。现在我们可以⼤胆推测, @Singular 注解为我们⽣成的 AbstractList实现类,并没有重写 add ⽅法。
为了验证我们的想法,反编译 Lombok 为我们编译后的 class ⽂件,进⾏查看,以下是简化后的代码:
protected PatientQuery(final PatientQuery.PatientQueryBuilder<?,?> b){
List patientIds;
switch(b.patientIds == null ?0: b.patientIds.size()){
case0:
patientIds = ptyList();
break;
case1:
patientIds = Collections.singletonList((Long)(0));
break;
default:
patientIds = Collections.unmodifiableList(new ArrayList(b.patientIds));
}
this.patientIds = patientIds;
}
可以看到啊,@Singular做了⼀些条件判断为我们实例化 patientIds 属性,
当 patientIds==null,使⽤ ptyList(); 初始化
当 patientIds.size()==1,使⽤ Collections.singletonList((Long)(0)); 初始化
当 patientIds.size() > 1,使⽤ Collections.unmodifiableList(new ArrayList(b.patientIds)); 初始化
但是不论哪种⽅式,最终使⽤ Collections ⽣成的 List 都是个不可变的 List,以ptyList(); 为例,会返回⼀个内部类的实例 EmptyList:
private static class EmptyList<E>
extends AbstractList<E>
implements RandomAccess, Serializable {
private static final long serialVersionUID =8842843931221139166L;
public Iterator<E>iterator(){
return emptyIterator();
}
public ListIterator<E>listIterator(){
return emptyListIterator();
}
public int size(){return0;}
public boolean isEmpty(){return true;}
public void clear(){}
public boolean contains(Object obj){return false;}
public boolean containsAll(Collection<?> c){return c.isEmpty();}
public Object[]toArray(){return new Object[0];}
public<T> T[]toArray(T[] a){
if(a.length >0)
a[0]= null;
return a;
}
public E get(int index){
throw new IndexOutOfBoundsException("Index: "+index);
}
public boolean equals(Object o){
return(o instanceof List)&&((List<?>)o).isEmpty();
}
public int hashCode(){return1;}
@Override
public boolean removeIf(Predicate<?super E> filter){
return false;
}
@Override
public void replaceAll(UnaryOperator<E> operator){
}
@Override
public void sort(Comparator<?super E> c){
}
// Override default methods in Collection
@Override
public void forEach(Consumer<?super E> action){
}
@Override
public Spliterator<E>spliterator(){ptySpliterator();}
// Preserves singleton property
private Object readResolve(){
抽象类的使用return EMPTY_LIST;
}
}
EmptyList 内部类继承了 AbstractList 抽象类,但并没有重写 add() ⽅法, 导致我们 PatientIds().add(1L);进⾏ add 操作时报 UnsupportedOperationException 异常。
到此,关于 @Singular注解使⽤时的问题都已经分析完了,⼤家在使⽤时⼀定谨慎⼩⼼,稍不注意就会导致 BUG。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。