java adapter(适配器)惯用方法
如果现在有一个Iterable类,你想要添加一种或多种在foreach语句中使用这个类的方法,例如方向迭代,应该怎么做呢? 如果之间继承这个类,并且覆盖iterator()方法,你只能替换现有的方法,而不能实现选择
一种解决方案是所谓的adapter方法的惯用法,"适配器"部分来自于设计模式,因为你必须提供特定接口以满足foreach语句,当你有一个接口并需要另一个接口时,编写adapter就可以解决问题,这里,希望在默认的前向迭代器的基础上,添加方向迭代器的能力,因此不能使用覆盖,而是添加了一个能够产生Iterable对象的方法,该对象可以用于foreach语句,正如你所见,可以提供多种使用foreach的方式
package object; //: holding/AdapterMethodIdiom.java // The "Adapter Method" idiom allows you to use foreach // with additional kinds of Iterables. import java.util.*; class ReversibleArrayList<T> extends ArrayList<T> { public ReversibleArrayList(Collection<T> c) { super(c); } public Iterable<T> reversed() {//返回一个具有反向迭代器的Iterable return new Iterable<T>() { public Iterator<T> iterator() { return new Iterator<T>() { int current = size() - 1; public boolean hasNext() { return current > -1; } public T next() { return get(current--); } public void remove() { // Not implemented throw new UnsupportedOperationException(); } }; } }; } } public class AdapterMethodIdiom { public static void main(String[] args) { ReversibleArrayList<String> ral = new ReversibleArrayList<String>( Arrays.asList("To be or not to be".split(" "))); // Grabs the ordinary iterator via iterator(): for(String s : ral) System.out.print(s + " "); System.out.println(); // Hand it the Iterable of your choice for(String s : ral.reversed()) System.out.print(s + " "); } } /* Output: To be or not to be be to not or be To *///:~
通过这种方法,可以在IterableClass.java的示例中添加两种适配器方法
package object; //: holding/MultiIterableClass.java // Adding several Adapter Methods. import java.util.*; public class MultiIterableClass extends IterableClass { public Iterable<String> reversed() { return new Iterable<String>() { public Iterator<String> iterator() { return new Iterator<String>() { int current = words.length - 1; public boolean hasNext() { return current > -1; } public String next() { return words[current--]; } public void remove() { // Not implemented throw new UnsupportedOperationException(); } }; } }; } public Iterable<String> randomized() { return new Iterable<String>() { public Iterator<String> iterator() { List<String> shuffled = new ArrayList<String>(Arrays.asList(words)); Collections.shuffle(shuffled, new Random(47));//// return shuffled.iterator(); } }; } public static void main(String[] args) { MultiIterableClass mic = new MultiIterableClass(); for(String s : mic.reversed()) System.out.print(s + " "); System.out.println(); for(String s : mic.randomized()) System.out.print(s + " "); System.out.println(); for(String s : mic) System.out.print(s + " "); } } /* Output: banana-shaped. be to Earth the know we how is that And is banana-shaped. Earth that how the be And we know to And that is how we know the Earth to be banana-shaped. *///:~
Collections方法并没有打乱原来的数组,而是打乱了shuffled中的引用,之所以这样,只是因为,randmize()方法用一个ArrayList将Arrays.asList()结果包装了起来
如果这个有Arrays.asList()产生的List被直接打乱,那么就会修改底层是数组,就像下面这样;
package object; //: holding/ModifyingArraysAsList.java import java.util.*; public class ModifyingArraysAsList { public static void main(String[] args) { Random rand = new Random(47); Integer[] ia = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; List<Integer> list1 = new ArrayList<Integer>(Arrays.asList(ia));//ia的输出被传给了ArrayList构造器,这将创建一个ia的元素引用的ArrayList System.out.println("Before shuffling: " + list1); Collections.shuffle(list1, rand); System.out.println("After shuffling: " + list1); System.out.println("array: " + Arrays.toString(ia)); List<Integer> list2 = Arrays.asList(ia);//lsit2 直接使用Arrays.asList(ia),这样做会直接修改ia的数据 System.out.println("Before shuffling: " + list2); Collections.shuffle(list2, rand); System.out.println("After shuffling: " + list2); System.out.println("array: " + Arrays.toString(ia)); } } /* Output: Before shuffling: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] After shuffling: [4, 6, 3, 1, 8, 7, 2, 5, 10, 9] array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] Before shuffling: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] After shuffling: [9, 1, 6, 3, 7, 2, 5, 10, 4, 8] array: [9, 1, 6, 3, 7, 2, 5, 10, 4, 8] *///:~
意识到Arrays.asLIst()产生的对象会使用底层数组作为其物理实现是很重要的,只要你执行的操作会修改这个List,并且你不想原来的数组被修改那么你就应该在另一个容器中创建副本