Skip to content

Latest commit

 

History

History
123 lines (102 loc) · 6.27 KB

03_Function.md

File metadata and controls

123 lines (102 loc) · 6.27 KB

《《《 返回首页
《《《 上一节

功能

函数模式将任意方法转换为对象。函数和相应方法之间的关系类似于 ComparatorcompareTo 方法之间的关系。

函数模式的通用版本演示了如何在方法声明的 throws 子句中使用类型变量。当一个类的不同实例包含可能引发不同检查异常的方法时,这可能很有用。

回想一下 Throwable 类有两个主要的子类 ExceptionError,其中第一个子类有另一个主要的子类 RuntimeException。检查异常是否是 RuntimeExceptionError 的子类。方法的 throws 子句可以列出 Throwable 的任何子类,但必须列出方法体可能抛出的任何检查异常,包括为正文中 调用的方法声明的任何检查异常。

9-5 给出了一个在 throws 子句中使用类型变量的例子。该示例定义了一个接口 Function<A,B,X>,它表示一个函数。接口包含一个方法 apply,它接受 A 类型的参数,返回类型 B 的结果,并且可以抛出类型的异常 X 。还有一个包含 applyAll方法的类。

9-5。在 throws 子句中输入参数

import java.util.*;
import java.lang.reflect.*;
interface Function<A, B, X extends Throwable> {
  public B apply(A x) throws X;
}
class Functions {
  public static <A, B, X extends Throwable> List<B> applyAll(Function<A, B, X> f, List<A> list) throws X {
    List<B> result = new ArrayList<B>(list.size());
    for (A x : list) 
      result.add(f.apply(x));
    return result;
  }
  public static void main(String[] args) { 
    Function<String, Integer, Error> length = new Function<String, Integer, Error>() {
      public Integer apply(String s) {
        return s.length();
      }
    };
    Function<String, Class<?>, ClassNotFoundException> forName = new Function<String, Class<?>, ClassNotFoundException>() {
      public Class<?> apply(String s) throws ClassNotFoundException {
        return Class.forName(s);
      }
    };
    Function<String, Method, Exception> getRunMethod = new Function<String, Method, Exception>() {
      public Method apply(String s) throws ClassNotFoundException,NoSuchMethodException {
        return Class.forName(s).getMethod("run");
      }
    };
    List<String> strings = Arrays.asList(args);
    System.out.println(applyAll(length, strings));
    try { 
      System.out.println(applyAll(forName, strings)); 
    } catch (ClassNotFoundException e) { 
      System.out.println(e); 
    }
    try { 
      System.out.println(applyAll(getRunMethod, strings)); 
    } catch (ClassNotFoundException e) { 
      System.out.println(e); 
    } catch (NoSuchMethodException e) { 
      System.out.println(e); 
    } catch (RuntimeException e) { 
      throw e; 
    } catch (Exception e) { 
      throw new AssertionError(); 
    }
  }
}

它接受 List<A> 类型的参数,返回 List<B> 类型的结果,并且可能再次引发 X 类型的异常; 该方法在参数列表的每个元素上调用 apply 方法来生成结果列 表。

该类的主要方法定义了这种类型的三个对象。 第一个是 Function<String,Integer,Error> 类型的长度。 它接受一个字符串并返回一个整数,它是给定字符串的长 度。 由于它没有引发检查异常,因此第三种类型设置为错误。(将其设置为 RuntimeException 也可以。)

第二个是 forName 类型的 Function<String,Class <?>,ClassNotFoundException>。 它接受一个字符串并返回一个类,即由给定字符串命名的类。 apply 方法可能会抛出一个 ClassNotFoundException,所以这被当作第三个类型参数。

第三个是 Function<String,Method,Exception> 类型的 getRunMethod。它接受一个字符串并返回一个方法,即在给定字符串所指定的类中名为 run 的方 法。该方法的主体可能会引发 ClassNotFoundExceptionNoSuchMethodException,因此第三个类型参数将被视为 Exception,这是包含这两个异常的最 小类。

最后一个例子显示了将泛型类型用于异常的主要限制。通常没有合适的类或接口包含函数可能引发的所有异常,因此您不得不重新使用异常,这太笼统,无法提供有用的 信息。

主要方法使用 applyAll 将三个函数中的每一个应用于字符串列表。三个调用中的每一个都被包装在一个 try 语句中,该语句适合于它可能抛出的异常。长度函数 没有 try 语句,因为它不引发检查异常。 forName 函数有一个带有 ClassNotFoundExceptioncatch 子句的 try 语句,它可能抛出一种异常。 getRunMethod 函数需要一个带有 catch 子句的 try 语句,用于 ClassNotFoundExceptionNoSuchMethodException,它可能抛出两种异常。但是 该函数被声明为抛出 Exception 类型,所以我们需要两个额外的“catchall”子句,一个重新抛出引发的任何运行时异常,另一个断言如果发生任何未处理的异常由前 三条规定。对于这个特定的例子,不需要重新提升运行时异常,但是如果可能有其他代码处理这些异常,这是一个好习惯。

例如,下面是典型的代码运行,打印长度列表,类别列表和方法列表(最后一个列表为了便于阅读而重新格式化,因为它不适合一行):

% java Functions java.lang.Thread java.lang.Runnable
[16, 18]
[class java.lang.Thread, interface java.lang.Runnable]
[public void java.lang.Thread.run(),
public abstract void java.lang.Runnable.run()]

这是一个引发 NoSuchMethodException 的运行,因为 java.util.List 没有 run 方法:

% java Functions java.lang.Thread java.util.List
[16, 14]
[class java.lang.Thread, interface java.util.List]
java.lang.NoSuchMethodException: java.util.List.run()

这是一个引发 ClassNotFoundException 的运行,因为没有名为 Fred 的类:

% java Functions java.lang.Thread Fred
[16, 4]
java.lang.ClassNotFoundException: Fred
java.lang.ClassNotFoundException: Fred

该异常会引发两次,一次是在应用 forName 时,一次是在应用 getRunMethod 时引发的。

《《《 下一节
《《《 返回首页