Java 8中使用了哪些功能接口?


154

我在Java 8中遇到了一个新术语:“功能接口”。在使用lambda表达式时,我只能找到它的一种用法。

Java 8提供了一些内置的功能接口,如果我们想定义任何功能接口,则可以使用@FunctionalInterface注释。它将允许我们在接口中仅声明一个方法。

例如:

@FunctionalInterface
interface MathOperation {
    int operation(int a, int b);
}

除了仅使用lambda表达式以外,它在Java 8中还有什么用?

这里的问题与我问的问题不同。它问的是为什么在使用lambda表达式时我们需要功能接口。我的问题是:为什么功能接口除了lambda表达式之外还有其他用途?)


1
该链接看起来好像是复制的。他们还讨论了为什么在功能接口中应该只有一种方法。stackoverflow.com/questions/33010594/…–
Kulbhushan Singh

1
@KulbhushanSingh我在发布之前就看到了这个问题...两个问题都感觉有所不同...
Madhusudan

Answers:


127

@FunctionalInterface批注对于检查代码的编译时间很有用。除了之外static,您不能使用多个方法,default并且抽象方法会覆盖Object@FunctionalInterface或任何其他接口中用作功能接口的方法。

但是,您可以使用不带此批注的lambda,也可以覆盖不带注解的方法 @Override注释的。

来自文档

功能接口只有一种抽象方法。由于默认方法具有实现,因此它们不是抽象的。如果接口声明了一个覆盖java.lang.Object的公共方法之一的抽象方法,则该方法也不计入接口的抽象方法计数,因为该接口的任何实现都将具有java.lang.Object或其他地方的实现。

可用于在lambda表达式:

public interface Foo {
  public void doSomething();
}

不能用于在lambda表达式:

public interface Foo {
  public void doSomething();
  public void doSomethingElse();
}

但这会产生编译错误

@FunctionalInterface
public interface Foo {
  public void doSomething();
  public void doSomethingElse();
}

无效的“ @FunctionalInterface”注释;Foo不是功能界面


43
更准确地说,您必须有一个完全不覆盖java.lang.Object功能接口中的方法的抽象方法。
Holger

9
...和它的“不拥有不止一个稍微不同的public方法,除了staticdefault” ......
霍尔格

4
仍然不知道拥有它的任何意义。为什么地球上有人会费心检查他/她的界面有多少种方法。标记接口仍然具有重点和特定目的。文档和答案仅说明其作用,而根本没有说明其用途。而“使用”正是OP所要求的。所以我不推荐这个答案。
saran3h

1
@VNT编译错误获取此接口的客户端,但接口本身不能更改。使用此注释,接口上会出现编译错误,因此,请确保没有人破坏您接口的客户端。
Sergii Bishyr

2
这显示了如何使用它们,但是没有解释为什么我们需要它们。
谢赫

14

文档确实在用途之间有所不同

内容丰富的注释类型,用于指示接口类型声明旨在成为Java语言规范定义的功能接口

和用例

请注意,可以使用lambda表达式,方法引用或构造函数引用来创建功能接口的实例。

其用语通常不排除其他用例。由于主要目的是指示功能接口,因此您的实际问题可以归结为除了lambda表达式和方法/构造函数引用之外功能接口是否还有其他用例?”

由于功能接口是Java语言规范定义的Java语言构造,因此只有该规范才能回答该问题:

JLS第9.8节。功能接口

除了通过声明和实例化类来创建接口实例的常规过程(第15.9节)之外,还可以使用方法引用表达式和lambda表达式(第15.13节,第15.27节)创建功能接口的实例。

因此,Java语言规范没有另外说明,该部分提到的唯一用例是使用方法引用表达式和lambda表达式创建接口实例。(这包括构造函数引用,因为它们在规范中被称为方法引用表达式的一种形式)。

因此,用一句话,不,Java 8中没有其他用例。


可能只是要求有点太多或不相关的(你不能选择的答案),但你会建议当有人创造了一个实用public static String generateTaskId()与使它更加“实用”别人选择将其写为public class TaskIdSupplier implements Supplier<String>get使用方法现有的一代实施。这是否是对功能接口的滥用,尤其是重复使用Supplier内置JDK中的?PS:我找不到更好的地方/问答。如果您愿意的话,很高兴迁移。
纳曼

1
@Naman创建命名类时,没有使实用程序方法更具功能性TaskIdSupplier。现在,问题是为什么创建命名类。在某些情况下,需要使用这种命名类型,例如,当您希望支持通过来查找实现时ServiceLoaderSupplier然后让它实现没有错。但是,当您不需要它时,请不要创建它。当您只需要一个时Supplier<String>,就已经足够使用DeclaringClass::generateTaskId并且消除对显式类的需求是该语言功能的重点。
Holger

老实说,我一直在为我提出的建议辩护。出于某种原因,我并没有真正觉得TaskIdSupplier实现值得付出努力,但是后来ServiceLoader我完全没想到。这些讨论,我们是具有如过程中遇到的一些问题有什么用的Supplierpublic存在,当一个人可以继续开发自己的界面?为什么没有public static Supplier<String> TASK_ID_SUPPLIER = () ->...作为一个全局变量?。(1/2)
Naman

1
@Naman用Java表示函数的惯用方式是方法,对这些函数进行评估与调用它们相同。不应该开发者被迫做variable.genericMethodName(args)的,而不是meaningfulMethodName(args)。无论是通过lambda表达式/方法引用还是通过手动创建的类,使用类类型表示函数都只是传递函数的工具(在Java中没有真正的函数类型)。仅应在需要时执行此操作。
Holger

1
当只有一个小的代码片段被传递时,可以创建一个lambda表达式来封装它。每当还需要像方法一样调用它时(这包括需要测试的场景,当代码片段不平凡时),创建可以调用的命名方法,并使用方法引用或lambda表达式/显式类封装呼叫,以便在需要时传递。常量仅在您不信任代码中嵌入的lambda表达式或方法引用的效率时才有用,换句话说,几乎不需要它们。
Holger

12

正如其他人所说,功能接口是公开一种方法的接口。它可能有多个方法,但所有其他方法必须具有默认实现。之所以称为“功能接口”,是因为它有效地充当了功能。由于您可以将接口作为参数传递,这意味着像函数式编程语言一样,函数现在是“一等公民”。这有很多好处,使用Stream API时,您会看到很多好处。当然,lambda表达式是它们的主要明显用法。


10

一点也不。Lambda表达式是该注释的唯一一点。


6
好吧,lamdbas也可以不带注释。这是一个断言,就像@Override让编译器知道您打算编写“功能性”内容一样(如果您滑入则出错)。
Thilo 2016年

1
直截了当和正确的答案,尽管有点短。我花时间添加了一个更详尽的答案,用更多的话说了同样的事情……
Holger 2016年

5

可以将lambda表达式分配给功能接口类型,但是方法引用和匿名类也可以分配给函数接口类型。

关于具体的功能接口的好处之一java.util.function是,他们可以组合起来创造新的功能(如Function.andThenFunction.composePredicate.and等),由于它们所包含的得心应手默认的方法。


您应该详细说明此评论。方法引用和新函数呢?
K.Nicholas

5

只有一种抽象方法的接口称为功能接口。使用@FunctionalInterface不是强制性的,但是最好的做法是将其与功能接口一起使用,以避免意外添加额外的方法。如果接口使用@FunctionalInterface注释进行注释,并且我们尝试使用多个抽象方法,则它将引发编译器错误。

package com.akhi;
    @FunctionalInterface
    public interface FucnctionalDemo {

      void letsDoSomething();
      //void letsGo();      //invalid because another abstract method does not allow
      public String toString();    // valid because toString from Object 
      public boolean equals(Object o); //valid

      public static int sum(int a,int b)   // valid because method static
        {   
            return a+b;
        }
        public default int sub(int a,int b)   //valid because method default
        {
            return a-b;
        }
    }

3

功能接口:

  • 在Java 8中引入
  • 包含“单个抽象”方法的接口。

范例1:

   interface CalcArea {   // --functional interface
        double calcArea(double rad);
    }           

范例2:

interface CalcGeometry { // --functional interface
    double calcArea(double rad);
    default double calcPeri(double rad) {
        return 0.0;
    }
}       

范例3:

interface CalcGeometry {  // -- not functional interface
    double calcArea(double rad);
    double calcPeri(double rad);
}   

Java8注释- @FunctionalInterface

  • 注释检查接口仅包含一种抽象方法。如果不是,请引发错误。
  • 即使缺少@FunctionalInterface,它仍然是功能接口(如果具有单个抽象方法)。注释有助于避免错误。
  • 功能接口可能具有其他静态和默认方法。
  • 例如Iterable <>,Comparable <>,Comparator <>。

功能接口的应用:

  • 方法参考
  • Lambda表达
  • 构造函数参考

要学习功能接口,请先学习接口中的默认方法,然后再学习功能接口后,将很容易理解方法参考和lambda表达式


您的前两个示例是否应该使用“抽象”关键字?
sofs1

1
@ sofs1在接口中声明的方法默认情况下是公共的和抽象的。如果要使用抽象类中的方法,则必须使用abstract关键字。但是,也可以对接口中的方法使用abstract关键字。他们允许它与旧版Java兼容,但不建议使用。
Ketan

2

您可以在Java 8中使用lambda

public static void main(String[] args) {
    tentimes(inputPrm - > System.out.println(inputPrm));
    //tentimes(System.out::println);  // You can also replace lambda with static method reference
}

public static void tentimes(Consumer myFunction) {
    for (int i = 0; i < 10; i++)
        myFunction.accept("hello");
}

有关Java LambdaFunctionalInterfaces的更多信息


1

@FunctionalInterface 是Java 8发行的新注释,它为lambda表达式提供目标类型,并且用于代码的编译时检查。

当您想使用它时:

1-您的界面不得具有多个抽象方法,否则将给出编译错误。

1-您的接口应该是纯接口,这意味着功能接口旨在由无状态类实现,纯Comparator接口的示例是接口,因为它不依赖于实现者状态,在这种情况下为给出编译错误,但是在许多情况下,您会将无法通过此类接口使用lambda

所述java.util.function包中包含各种通用功能的接口,如PredicateConsumerFunction,和Supplier

另外请注意,您可以使用不带此批注的lambda。


1

除了其他答案,我认为“为什么要使用功能接口而不是直接与lambda表达式一起使用”的主要原因可能与面向对象的Java语言的性质有关。

Lambda表达式的主要属性是:1.它们可以传递2.可以在将来的特定时间(几次)执行。现在,为了以语言支持此功能,其他一些语言仅处理此问题。

例如,在Java Script中,一个函数(匿名函数或Function文字)可以作为一个对象来寻址。因此,您可以简单地创建它们,也可以将它们分配给变量等等。例如:

var myFunction = function (...) {
    ...;
}
alert(myFunction(...));

或通过ES6,您可以使用箭头功能。

const myFunction = ... => ...

到目前为止,Java语言设计人员尚未接受通过这些方式(功能编程技术)来处理所提到的功能。他们认为Java语言是面向对象的,因此他们应该通过面向对象的技术解决此问题。他们不想错过Java语言的简单性和一致性。

因此,它们使用接口,就像只需要一个方法(我的意思是功能接口)的接口的对象时,您可以用lambda表达式替换它。如:

ActionListener listener = event -> ...;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.