适配器模式用来将一个类的接口转换为另一种接口,使本来因为接口不兼容而不能一起工作的类可以一起工作。一个很常见的例子就是如果想要手机和U盘通信,但是两者没有通用的接口,就需要通过OTG线来连接,OTG线一端是micro-usb接口,另一端是USB接口,分别对应了手机和U盘的接口。
而具体的实现有两种,一种叫类适配器模式,另一种叫对象适配器模式。
类适配器模式
Target是客户需要的接口,Adapter是适配器类,来做一个转换,Adaptee是需要适配的类,最终的目的是想要客户可以使用它。图中其实应该画上客户,客户通过调用Target接口,就可以使用Adaptee类。
Adapter通过继承实现了Target的接口,Adapter和Adaptee如何产生联系,有两种方式,一是使用组合的方式,即把Adaptee对象放在Adapter中,即类适配器模式;另一种方式是多重继承,即让Adapter也继承Adaptee。
对象适配器模式
public class Client {
public static void main(String[] args){
Target target = new Adapter(new Adaptee());
target.method1();
}
}
interface Target{
public void method1();
}
class Adaptee{
public void method2(){
System.out.println("execute Adaptee.method2()");
}
}
class Adapter implements Target{
private Adaptee adaptee;
public Adapter(Adaptee adaptee){
this.adaptee = adaptee;
}
@Override
public void method1(){
this.adaptee.method2();
}
}
Client通过Adapter类的Target接口调用了Adaptee对象。
Arrays.asList()
jdk中的Arrays.asList()方法用到了适配器模式。
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
假如现在有一个数组,或者一系列元素,然而需要一个List对象,这时就可以使用这个方法得到一个固定长度的list(长度即数组长度)。在这里用到了可变参数,实际上是java的一个语法糖,实际上还是通过数组来实现。这个方法返回的是一个ArrayList对象,通过数组a来进行初始化。但是需要注意的是,这个ArrayList并不是java.util.ArrayList,而是在Arrays中实现的一个内部类。
传入到构造函数中的对象数组,进行了一个Null Pointer的检查,如果是null则会抛出异常,如果不是,则赋值给了a。这样,内部类对象的成员a实际指向了作为参数传入的对象数组,对a的操作,实际上就是对数组的操作。但是观察这个内部类的成员函数,并没有add、remove方法,实际上也很好理解,因为本质上维护的还是一个数组,这些方法本来也无法实现。
总结一下,List为客户需要的类,数组为被适配对象,通过Arrays.ArrayList作为适配器,实现了接口转换,但背后还是数组。
这样一来,得到了Arrays.asList()的几个特点:
1. 参数不能是基本类型,因为用到了泛型,必须传入一个对象类型。
2. 方法返回的List,是与数组链接起来的,即更新一处,则同时更新,因为背后本来就是一个数组
3. 不能使用集合修改的相关方法 add/remove/clear,原因与第二条相同。
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);
}
}