Skip to content

JasonMing/java-function

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Java SAM Function Extensions

目标

扩展java.util.function下的SAM Function功能。

简述

模型

命名规则

由于Java不支持泛型重载,所以对所有函数式接口采用以下命名规则:

函数类别 + 异常标识 + 参数个数

  • 函数类别

    主要分为Func<..., R>Action<...>Func有返回值,Action无返回值(void)。

  • 异常标识

    标识 描述
    无标识 对应方法不能抛出异常
    E 对应方法可以抛出任意Exception
    X 可通过泛型参数X指定具体类型的异常
  • 参数个数

    当前支持0~个入参,0个参数(无参)时后缀不加0

主要类型

函数原型 异常声明 函数类型 入参个数
void invokeV(...) /* none */ Action 0~9个,如:
  • Action - 无参,无返回值的函数(无参时无后缀)
  • Func - 无参,返回值类型为R的函数(无参时无后缀)
  • Func2 - 2个入参,返回值类型为R的函数
  • FuncE2 - 2个入参,返回值类型为R,有异常抛出声明的函数
  • FuncX2 - 2个入参,返回值类型为R,有异常类型为X抛出声明的函数
throws Exception ActionE
throws X (X extends Throwable) ActionX
R invoke(...) /* none */ Func
throws Exception FuncE
throws X (X extends Throwable) FuncX

java.util.function兼容

特性

Partial apply(部分参数应用)

只应用部分参数并返回剩余参数的Function

public static void main(String[] args)
{
    final Func3<Integer, Integer, Integer, Boolean> f3 = (p1, p2, p3) -> (p1 + p2 + p3) % 2 == 0;
    
    final Func2<Integer, Integer, Boolean>          f2 = f3.apply(3);
    final Func1<Integer, Boolean>                   f1 = f2.apply(13);
    
    f1.invoke(4); // (3 + 13 + 4) % 2 = 0 => true
    f1.invoke(7); // (3 + 13 + 7) % 2 = 1 => false
}

可抛出异常的方法

java标准库里所有的函数式接口均不能抛出异常,一旦使用到抛出检查型异常(checked exception)的方法时,就无可避免地进行try-catch-rethrow的操作,不但繁琐,而且很有可能误吞异常。

使用名称中带有XE的函数式接口(如:FuncX/ActionE)即可简单地通过声明形式将异常向外传播,而无需进行手动处理。

static String readSomething() throws IOException { ... }

public static void main(final String[] args)
{
    // 使用标准库的Supplier<T>,异常声明不兼容
    final Supplier<String> error = () -> readSomething(); // error: "Unhandled exception: IOException"

    // 使用标准库的Supplier<T>,包装并重抛出异常
    final Supplier<String> supplier = () ->
    {
        try
        {
            return readSomething();
        } catch (final IOException e)
        {
            // 必须重新包装为非检查型异常(unchecked exception)抛出,或自行处理异常。
            throw new RuntimeException(e);
        }
    };
    
    // 使用FuncX<R, X>,异常直接向外传播
    final FuncX<String, IOException> funcX = () -> readSomething();
    
    // 如果不关注异常类型,可直接使用FuncE<R>
    final FuncE<String> funcE = () -> readSomething();
}

传播异常

在上述例子可抛出异常的方法中,可以使用FuncE<R>FuncX<R, X>调用抛出检查型异常(checked exception)的方法而无需进行额外的异常处理。

但大部分时候,标准库中使用的是无异常声明的Supplier<T>Function<T, R>,但执行过程中很多时候明知异常是需要抛出到上一级进行处理,但由于方法声明的限制,我们必须通过RuntimeException(或其子类)重新包装后再抛出,使用起来甚是不便。

// 抛出检查型异常的方法
static Map<String, ?> readJsonWithException(String source) throws IOException { ... }

public static void main(String[] args)
{
    final List<String> source = new ArrayList();

    source.stream()
          .map(x -> readJsonWithException(x)) // 编译错误:Unhandled exception
          .collect(Collectors.toList());

    // 因为map()使用的是java.util.function.Function<T,R>,其方法签名为:
    // `R apply(T t);`,不可抛出任何检查型异常
    // 所以,直接调用`readJson()`方法时会提示编译错误:"Unhandled exception"
    // 此时就必须手动捕获异常,但大多数情况下均无法处理异常,此时就需要使用非检查型异常进行包装后从新抛出。

    source.stream()
          .map(x ->
          {
              try
              {
                  return readJsonWithException(x);
              } catch(IOException e)
              {
                  throw new RuntimeException(e);
              }
          })
          .collect(Collectors.toList());
}

此时我们可以使用Functions.propagate()方法,生成一个可正常传播异常但方法上不声明抛出的Action/Func

static Map<String, ?> readJson(String source) throws IOException { ... }

public static void main(String[] args)
{
    final List<String> source = new ArrayList();

    // 使用Functions.propagate()对外传播异常
    source.stream()
          .map(Functions.propagate(x -> readJson(x)))
          .collect(Collectors.toList());
}

柯里化

通过Partial apply(部分参数应用) 实现部分柯里化。

About

Extensions for java.util.funcion, allow converting between void and non-void functions and exception propagation.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published