Java8中功能接口的用途

时间:2017-03-27 15:37:01

标签: java java-8 functional-interface

关于Java8 内置功能接口,我遇到过很多问题,包括thisthisthis和{{3} }。但所有人都在问“为什么只有一种方法?”或“如果我使用我的功能界面执行X,为什么会出现编译错误”等等。我的问题是:这些新功能接口的存在目的是什么,当我可以在我自己的界面中使用lambdas时

考虑this中的以下示例代码:

    // Approach 6: print using a predicate
     public static void printPersonsWithPredicate(List<Person> roster, 
                                                  Predicate<Person> tester) {
            for (Person p : roster) {
                if (tester.test(p)) {
                   System.out.println(p);
                }
            }
        }

好的,很好,但这可以通过上面的自己的例子来实现(使用单一方法的界面并不新鲜):

      // Approach 5: 
        public static void printPersons(<Person> roster, 
                                        CheckPerson tester) {
            for (Person p : roster) {
                if (tester.test(p)) {
                   System.out.println(p);
                }
            }
        }


  interface CheckPerson {
        boolean test(Person p);
    }

我可以将lambda传递给两个方法。

第一种方法为我节省了一个自定义界面。 这是吗?

或者这些标准功能接口(消费者,供应商,谓词,功能)是否可以作为代码组织,可读性,结构,[其他]的模板?

3 个答案:

答案 0 :(得分:5)

虽然你问“是吗?”,但是我们不需要在我们想要lambda类型的时候编写新接口,这是非常好的。

问问自己,如果您正在阅读API,这对程序员来说更容易使用:

public void processUsers(UserProcessor userProcessor);

......或......

public void processUsers(Consumer<User> userProcessor);

对于前者,我必须去看看UserProcessor以找出它是什么,以及我如何创造一个;我甚至不知道它可以作为一个lambda实现,直到我去找出来。对于后者,我立即知道我可以键入u -> System.out.println(u),我将通过将用户写入stdout来处理用户。

此外图书馆的作者不需要使用Yet Another Type来扩展他们的库。

此外,如果我将lambda强制转换为功能类型,我可以使用该类型的合成方法,例如:

 candidates.filter( personPredicates.IS_GRADUATE.negate());

这为您提供Predicate方法and()or()negate(); Function方法compose()andThen() - 除非您实施,否则您的自定义类型不会有。

答案 1 :(得分:4)

显然,您可以跳过使用这些新界面,并使用更好的名称滚动自己的界面。但是有一些注意事项:

  1. 除非您的自定义界面扩展了其中一个内置插件,否则您将无法在其他JDK API中使用自定义界面。
  2. 如果你总是和自己一起滚动,那么在某些时候你会遇到一个你无法想到一个好名字的情况。例如,我认为CheckPerson对于其目的来说并不是一个好名字,尽管这是主观的。
  3. 大多数内置接口还定义了一些其他API。例如,Predicate定义or(Predicate)and(Predicate)negate()

    Function定义andThen(Function)compose(Function)等。

    它不是特别令人兴奋,直到它:在函数上使用除抽象之外的方法允许更容易的组合,策略选择等等,例如(使用this article中建议的样式):< / p>

    在:

    class PersonPredicate {
      public Predicate<Person> isAdultMale() {
        return p -> 
                p.getAge() > ADULT
                && p.getSex() == SexEnum.MALE;
      }
    }
    

    可能会变成这样,最终可以重复使用:

    class PersonPredicate {
      public Predicate<Person> isAdultMale() {
        return isAdult().and(isMale());
      }
    
      publci Predicate<Person> isAdultFemale() {
        return isAdult().and(isFemale());
      }
    
      public Predicate<Person> isAdult() {
        return p -> p.getAge() > ADULT;
      }
    
      public Predicate<Person> isMale() {
        return isSex(SexEnum.MALE);
      }
      public Predicate<Person> isFemale() {
        return isSex(SexEnum.FEMALE);
      }
      public Predicate<Person> isSex(SexEnum sex) {
        return p -> p.getSex() == sex;
      }
    }
    

答案 2 :(得分:0)

Java API为Java开发人员提供了许多内置的函数接口。我们可以多次使用内置的函数接口。但有两个理由使用客户功能界面。

  1. 使用客户功能界面明确描述了什么。

    让我们假设您在构造函数上有一个带有名称参数的类User。当您使用内置函数接口来引用构造函数时,代码如下所示:

    Function<String,User> userFactory=User::new;
    

    如果您想要清楚地描述它,您可以引入自己的功能接口,例如:UserFactory;

    UserFactory userFactory=User::new;
    

    由于内置函数接口而使用自定义函数接口的另一个原因在某些地方感到困惑。当您看到类型为Function<String,User>的参数时,它是创建新用户还是从数据库中查询用户或通过字符串删除用户并返回用户,...?如果您使用的是确切的函数接口它正在做什么,因为UserFactory是从字符串用户名创建用户。

  2. 使用Customer Function Interface在java内置函数接口中处理已检查的Exception。

    由于内置的​​函数接口无法抛出已检查的异常,因此当您处理lambda表达式中可能引发已检查异常的内容时会出现问题,但您不想使用try / catch用于处理已检查的Exception,这将导致许多代码难以在lambda表达式中读取。然后您可以定义自己的函数接口,该函数接口抛出任何CheckedException并在使用Java API时将其调整为内置函数接口代码如下:

    //if the bars function throws a checked Exception,you must catch the exception
    Stream.of(foos).map(t->{
       try{
          return bars.apply(t);
       }catch(ex){//handle exception}
    });
    

    你也可以定义自己的函数接口抛出一个已检查的异常,然后将其调整为内置函数,让我们说它是一个Mapping接口,代码如下:

    interface Mapping<T,R> {
      R apply(T value) throws Exception;
    }
    
    private Function<T,R> reportsErrorWhenMappingFailed(Mapping<T,R> mapping){
      return (it)->{
        try{
          return mapping.apply(it);
        }catch(ex){
          handleException(ex);
          return null;
        }
      };
    } 
    Stream.of(foos).map(reportsErrorWhenMappingFailed(bars));