前言
最近做项目,遇到了以前的代码,使用Collections.emptySet,然后add了,结果不言而喻,异常throw。想起了以前的事Arrays.asList()方法。看了下源码,实现的木得差不多,但实现方式略有差异。
1. Collections.empty
这个类的这种方法还有很多,无一例外返回一个非null的Set Map List......然而这些Set Map等其实是private的内部类。以emptySet()为例。
public static final <T> Set<T> emptySet() {
return (Set<T>) EMPTY_SET;
}
public static final Set EMPTY_SET = new EmptySet<>();
可以看到是一个final对象,这正是这个bean给我们的定义,单例模式,进入类
private static class EmptySet<E>
extends AbstractSet<E>
implements Serializable
{
private static final long serialVersionUID = 1582296315990362920L;
public Iterator<E> iterator() { return emptyIterator(); }
public int size() {return 0;}
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;
}
// Override default methods in Collection
@Override
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
}
@Override
public boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
return false;
}
@Override
public Spliterator<E> spliterator() { return Spliterators.emptySpliterator(); }
// Preserves singleton property
private Object readResolve() {
return EMPTY_SET;
}
@Override
public int hashCode() {
return 0;
}
}
可以看到,根本没有Map存储Set的key,啥都是空的,但是又不为null。它的add方法在继承类中AbstractSet<E>的继承类AbstractCollection<E>中,直接抛出异常
public boolean add(E e) {
throw new UnsupportedOperationException();
}
显然,JDK是想我们用来定义一个非null的不能添加元素的空集合,而笔者之前的代码,没注意就在后面的逻辑add元素了,然后
throw new UnsupportedOperationException();
而在Collections中有很多这类型的定义,意义同理。
2. Arrays.asList
之所以说这个,主要是结果表现同现象。不能修改,但结果可以不为empty。
上源码,当然这里不是单例了。
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
这里的ArrayList很具迷惑性,但实际是一个private的内部类
private static class ArrayList<E> extends AbstractList<E>
implements RandomAccess, java.io.Serializable
{
private static final long serialVersionUID = -2764017481108945198L;
private final E[] a;
ArrayList(E[] array) {
a = Objects.requireNonNull(array);
}
@Override
public int size() {
return a.length;
}
@Override
public Object[] toArray() {
return Arrays.copyOf(a, a.length, Object[].class);
}
@Override
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
int size = size();
if (a.length < size)
return Arrays.copyOf(this.a, size,
(Class<? extends T[]>) a.getClass());
System.arraycopy(this.a, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
@Override
public E get(int index) {
return a[index];
}
@Override
public E set(int index, E element) {
E oldValue = a[index];
a[index] = element;
return oldValue;
}
@Override
public int indexOf(Object o) {
E[] a = this.a;
if (o == null) {
for (int i = 0; i < a.length; i++)
if (a[i] == null)
return i;
} else {
for (int i = 0; i < a.length; i++)
if (o.equals(a[i]))
return i;
}
return -1;
}
@Override
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
@Override
public Spliterator<E> spliterator() {
return Spliterators.spliterator(a, Spliterator.ORDERED);
}
@Override
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
for (E e : a) {
action.accept(e);
}
}
@Override
public void replaceAll(UnaryOperator<E> operator) {
Objects.requireNonNull(operator);
E[] a = this.a;
for (int i = 0; i < a.length; i++) {
a[i] = operator.apply(a[i]);
}
}
@Override
public void sort(Comparator<? super E> c) {
Arrays.sort(a, c);
}
@Override
public Iterator<E> iterator() {
return new ArrayItr<>(a);
}
}
它不能add的原因是,元素赋值即固定为常量
private final E[] a;
同理没有覆写add方法, 用的也是AbstractCollection<E>,直接抛出异常
public boolean add(E e) {
throw new UnsupportedOperationException();
}
显然JDK想我们asList后就不要修改,避免不可预知的修改数据不一致的问题
3. 匿名内部类
public class Test {
public static void main(String[] args) {
final String str = "test";
new Thread(
new Runnable() {
@Override
public void run() {
System.out.println(str);
}
}
).start();
}
}
可以看出匿名内部类的局部变量必须是final类型,这是JDK保证数据一致性的一种方式
可以看出内部类会编译出一个新的class文件
同理lambda表达式的局部变量仍然必须final,但编译的class变成了一个
总结
笔者最开始没仔细看,也没看出为啥报错了 。调试代码才发现前面的业务已经埋坑了。当然可能还有很多其他类这样写,但要明白这些类的用途,与以后业务的交付,才能正常的发挥作用。