💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
### [适配器方法惯用法](https://lingcoder.gitee.io/onjava8/#/book/12-Collections?id=%e9%80%82%e9%85%8d%e5%99%a8%e6%96%b9%e6%b3%95%e6%83%af%e7%94%a8%e6%b3%95) 如果现在有一个**Iterable**类,你想要添加一种或多种在*for-in*语句中使用这个类的方法,应该怎么做呢?例如,你希望可以选择正向还是反向遍历一个单词列表。如果直接继承这个类,并覆盖`iterator()`方法,则只能替换现有的方法,而不能实现遍历顺序的选择。 一种解决方案是所谓*适配器方法*(Adapter Method)的惯用法。“适配器”部分来自于设计模式,因为必须要提供特定的接口来满足*for-in*语句。如果已经有一个接口并且需要另一个接口时,则编写适配器就可以解决这个问题。 在这里,若希望在默认的正向迭代器的基础上,添加产生反向迭代器的能力,因此不能使用覆盖,相反,而是添加了一个能够生成**Iterable**对象的方法,该对象可以用于*for-in*语句。这使得我们可以提供多种使用*for-in*语句的方式: ~~~ // collections/AdapterMethodIdiom.java // The "Adapter Method" idiom uses for-in // with additional kinds of Iterables import java.util.*; class ReversibleArrayList<T> extends ArrayList<T> { ReversibleArrayList(Collection<T> c) { super(c); } public Iterable<T> reversed() { 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 */ ~~~ 在主方法中,如果直接将**ral**对象放在*for-in*语句中,则会得到(默认的)正向迭代器。但是如果在该对象上调用`reversed()`方法,它会产生不同的行为。 通过使用这种方式,可以在**IterableClass.java**示例中添加两种适配器方法: ~~~ // collections/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. */ ~~~ 注意,第二个方法`random()`没有创建它自己的**Iterator**,而是直接返回被打乱的**List**中的**Iterator**。 从输出中可以看到,`Collections.shuffle()`方法不会影响到原始数组,而只是打乱了**shuffled**中的引用。之所以这样,是因为`randomized()`方法用一个**ArrayList**将`Arrays.asList()`的结果包装了起来。如果这个由`Arrays.asList()`生成的**List**被直接打乱,那么它将修改底层数组,如下所示: ~~~ // collections/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<>(Arrays.asList(ia)); 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); 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()`的输出被传递给了**ArrayList**的构造器,这将创建一个引用**ia**的元素的**ArrayList**,因此打乱这些引用不会修改该数组。但是,如果直接使用`Arrays.asList(ia)`的结果,这种打乱就会修改**ia**的顺序。重要的是要注意`Arrays.asList()`生成一个**List**对象,该对象使用底层数组作为其物理实现。如果执行的操作会修改这个**List**,并且不希望修改原始数组,那么就应该在另一个集合中创建一个副本。