|
| 1 | +## 功能 |
| 2 | + |
| 3 | +函数模式将任意方法转换为对象。函数和相应方法之间的关系类似于 `Comparator` 和 `compareTo` 方法之间的关系。 |
| 4 | + |
| 5 | +函数模式的通用版本演示了如何在方法声明的 `throws` 子句中使用类型变量。当一个类的不同实例包含可能引发不同检查异常的方法时,这可能很有用。 |
| 6 | + |
| 7 | +回想一下 `Throwable` 类有两个主要的子类 `Exception` 和 `Error`,其中第一个子类有另一个主要的子类 `RuntimeException`。检查异常是否是 `RuntimeException` 或 `Error` 的子类。方法的 `throws` 子句可以列出 `Throwable` 的任何子类,但必须列出方法体可能抛出的任何检查异常,包括为正文中调用的方法声明的任何检查异常。 |
| 8 | + |
| 9 | +例 `9-5` 给出了一个在 `throws` 子句中使用类型变量的例子。该示例定义了一个接口 `Function<A,B,X>`,它表示一个函数。接口包含一个方法 `apply`,它接受 `A` 类型的参数,返回类型 `B` 的结果,并且可以抛出类型的异常 `X` 。还有一个包含 `applyAll`方法的类。 |
| 10 | + |
| 11 | +例 `9-5`。在 `throws` 子句中输入参数 |
| 12 | + |
| 13 | +```java |
| 14 | + import java.util.*; |
| 15 | + import java.lang.reflect.*; |
| 16 | + interface Function<A, B, X extends Throwable> { |
| 17 | + public B apply(A x) throws X; |
| 18 | + } |
| 19 | + class Functions { |
| 20 | + public static <A, B, X extends Throwable> List<B> applyAll(Function<A, B, X> f, List<A> list) throws X { |
| 21 | + List<B> result = new ArrayList<B>(list.size()); |
| 22 | + for (A x : list) |
| 23 | + result.add(f.apply(x)); |
| 24 | + return result; |
| 25 | + } |
| 26 | + public static void main(String[] args) { |
| 27 | + Function<String, Integer, Error> length = new Function<String, Integer, Error>() { |
| 28 | + public Integer apply(String s) { |
| 29 | + return s.length(); |
| 30 | + } |
| 31 | + }; |
| 32 | + Function<String, Class<?>, ClassNotFoundException> forName = new Function<String, Class<?>, ClassNotFoundException>() { |
| 33 | + public Class<?> apply(String s) throws ClassNotFoundException { |
| 34 | + return Class.forName(s); |
| 35 | + } |
| 36 | + }; |
| 37 | + Function<String, Method, Exception> getRunMethod = new Function<String, Method, Exception>() { |
| 38 | + public Method apply(String s) throws ClassNotFoundException,NoSuchMethodException { |
| 39 | + return Class.forName(s).getMethod("run"); |
| 40 | + } |
| 41 | + }; |
| 42 | + List<String> strings = Arrays.asList(args); |
| 43 | + System.out.println(applyAll(length, strings)); |
| 44 | + try { |
| 45 | + System.out.println(applyAll(forName, strings)); |
| 46 | + } catch (ClassNotFoundException e) { |
| 47 | + System.out.println(e); |
| 48 | + } |
| 49 | + try { |
| 50 | + System.out.println(applyAll(getRunMethod, strings)); |
| 51 | + } catch (ClassNotFoundException e) { |
| 52 | + System.out.println(e); |
| 53 | + } catch (NoSuchMethodException e) { |
| 54 | + System.out.println(e); |
| 55 | + } catch (RuntimeException e) { |
| 56 | + throw e; |
| 57 | + } catch (Exception e) { |
| 58 | + throw new AssertionError(); |
| 59 | + } |
| 60 | + } |
| 61 | + } |
| 62 | +``` |
| 63 | + |
| 64 | +它接受 `List<A>` 类型的参数,返回 `List<B>` 类型的结果,并且可能再次引发 `X` 类型的异常; 该方法在参数列表的每个元素上调用 `apply` 方法来生成结果列表。 |
| 65 | + |
| 66 | +该类的主要方法定义了这种类型的三个对象。 第一个是 `Function<String,Integer,Error>` 类型的长度。 它接受一个字符串并返回一个整数,它是给定字符串的长度。 由于它没有引发检查异常,因此第三种类型设置为错误。(将其设置为 `RuntimeException` 也可以。) |
| 67 | + |
| 68 | +第二个是 `forName` 类型的 `Function<String,Class <?>,ClassNotFoundException>`。 它接受一个字符串并返回一个类,即由给定字符串命名的类。 `apply` 方法可能会抛出一个 `ClassNotFoundException`,所以这被当作第三个类型参数。 |
| 69 | + |
| 70 | +第三个是 `Function<String,Method,Exception>` 类型的 `getRunMethod`。它接受一个字符串并返回一个方法,即在给定字符串所指定的类中名为 `run` 的方法。该方法的主体可能会引发 `ClassNotFoundException` 或 `NoSuchMethodException`,因此第三个类型参数将被视为 `Exception`,这是包含这两个异常的最小类。 |
| 71 | + |
| 72 | +最后一个例子显示了将泛型类型用于异常的主要限制。通常没有合适的类或接口包含函数可能引发的所有异常,因此您不得不重新使用异常,这太笼统,无法提供有用的信息。 |
| 73 | + |
| 74 | +主要方法使用 `applyAll` 将三个函数中的每一个应用于字符串列表。三个调用中的每一个都被包装在一个 `try` 语句中,该语句适合于它可能抛出的异常。长度函数没有 `try` 语句,因为它不引发检查异常。 `forName` 函数有一个带有 `ClassNotFoundException` 的 `catch` 子句的 `try` 语句,它可能抛出一种异常。`getRunMethod` 函数需要一个带有 `catch` 子句的 `try` 语句,用于 `ClassNotFoundException` 和 `NoSuchMethodException`,它可能抛出两种异常。但是该函数被声明为抛出 `Exception` 类型,所以我们需要两个额外的“catchall”子句,一个重新抛出引发的任何运行时异常,另一个断言如果发生任何未处理的异常由前三条规定。对于这个特定的例子,不需要重新提升运行时异常,但是如果可能有其他代码处理这些异常,这是一个好习惯。 |
| 75 | + |
| 76 | +例如,下面是典型的代码运行,打印长度列表,类别列表和方法列表(最后一个列表为了便于阅读而重新格式化,因为它不适合一行): |
| 77 | + |
| 78 | +```java |
| 79 | + % java Functions java.lang.Thread java.lang.Runnable |
| 80 | + [16, 18] |
| 81 | + [class java.lang.Thread, interface java.lang.Runnable] |
| 82 | + [public void java.lang.Thread.run(), |
| 83 | + public abstract void java.lang.Runnable.run()] |
| 84 | +``` |
| 85 | + |
| 86 | +这是一个引发 `NoSuchMethodException` 的运行,因为 `java.util.List` 没有 `run` 方法: |
| 87 | + |
| 88 | +```java |
| 89 | + % java Functions java.lang.Thread java.util.List |
| 90 | + [16, 14] |
| 91 | + [class java.lang.Thread, interface java.util.List] |
| 92 | + java.lang.NoSuchMethodException: java.util.List.run() |
| 93 | +``` |
| 94 | + |
| 95 | +这是一个引发 `ClassNotFoundException` 的运行,因为没有名为 `Fred` 的类: |
| 96 | + |
| 97 | +```java |
| 98 | + % java Functions java.lang.Thread Fred |
| 99 | + [16, 4] |
| 100 | + java.lang.ClassNotFoundException: Fred |
| 101 | + java.lang.ClassNotFoundException: Fred |
| 102 | +``` |
| 103 | + |
| 104 | +该异常会引发两次,一次是在应用 `forName` 时,一次是在应用 `getRunMethod` 时引发的。 |
| 105 | + |
| 106 | + |
| 107 | + |
| 108 | + |
| 109 | + |
| 110 | + |
| 111 | + |
| 112 | + |
| 113 | + |
| 114 | + |
| 115 | + |
| 116 | + |
| 117 | + |
| 118 | + |
| 119 | + |
| 120 | + |
| 121 | + |
| 122 | + |
| 123 | + |
| 124 | + |
| 125 | + |
| 126 | + |
| 127 | + |
| 128 | + |
| 129 | + |
| 130 | + |
| 131 | + |
| 132 | + |
| 133 | + |
| 134 | + |
| 135 | + |
0 commit comments