Java 8에서 사용되는 기능 인터페이스는 무엇입니까?


154

Java 8에서 "기능적 인터페이스"라는 새로운 용어를 발견했습니다. 람다 식으로 작업하는 동안 하나만 사용할 수있었습니다 .

Java 8은 일부 내장 기능 인터페이스를 제공하며 기능 인터페이스를 정의하려는 경우 @FunctionalInterface주석 을 사용할 수 있습니다 . 인터페이스에서 단일 메소드 만 선언 할 수 있습니다.

예를 들면 다음과 같습니다.

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

람다 식으로 작업하는 것 외에 Java 8에서는 얼마나 유용 합니까?

(질문 여기가 내가 물었다 것과 다른 그것은 람다 식으로 작업하는 동안 우리는 기능적인 인터페이스가 필요한 이유를 묻는 내 질문은 :.. 다른 용도로는 기능 인터페이스는 람다 표현식 외에해야 할 이유는 무엇입니까?)


1
이 링크가 중복 된 것 같습니다. 또한 Functional Interface에 메소드가 하나만 있어야하는 이유에 대해서도 설명합니다. stackoverflow.com/questions/33010594/…
Kulbhushan Singh

1
@KulbhushanSingh 게시하기 전에이 질문을 보았습니다 ... 두 질문 모두 차이가 있습니다 ...
Madhusudan

답변:


127

@FunctionalInterface주석은 코드의 컴파일 시간 확인에 유용합니다. 둘 이상의 외에 방법이 없다 static, default그리고 추상적 인 방법을한다는 점에서 재정의 방법 Object@FunctionalInterface또는 기능적 인터페이스로 사용되는 다른 인터페이스.

그러나이 주석없이 람다를 사용할 수 있으며 주석없이 메소드를 재정의 할 수 있습니다 @Override.

문서에서

기능적 인터페이스에는 정확히 하나의 추상 메소드가 있습니다. 기본 메소드에는 구현이 있으므로 추상적이지 않습니다. 인터페이스가 java.lang.Object의 공개 메소드 중 하나를 대체하는 추상 메소드를 선언하는 경우 인터페이스의 모든 구현은 java.lang.Object 또는 다른 곳에서 구현을 가지므로 인터페이스의 추상 메소드 수에도 포함되지 않습니다.

이것은 사용할 수 있습니다 람다 식에 :

public interface Foo {
  public void doSomething();
}

이 작업은 사용할 수 없습니다 람다 식에 :

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방법 외에 static하고 default..."
홀거

4
여전히 이해해야 할 점이 없습니다. 왜 지구상의 누군가가 자신의 인터페이스가 얼마나 많은 방법을 가지고 있는지 확인하는 것을 귀찮게 할 것입니다. 마커 인터페이스에는 여전히 특정 목적이 있습니다. 문서와 답변은 그것이 어떻게 사용되는지가 아니라 그 기능을 설명합니다. "사용"은 정확히 OP가 요구 한 것입니다. 따라서이 답변을 권장하지 않습니다.
saran3h

1
@VNT 컴파일 오류는이 인터페이스의 클라이언트를 가져 오지만 인터페이스 자체는 변경할 수 없습니다. 이 주석을 사용하면 컴파일 오류가 인터페이스에 있으므로 아무도 인터페이스의 클라이언트를 중단하지 않아야합니다.
Sergii Bishyr

2
이것은 그것들을 사용하는 방법을 보여 주지만 왜 우리가 필요한지 설명하지 않습니다.
셰이크

14

문서는 참 목적 사이의 차이를 만든다

인터페이스 유형 선언이 Java 언어 사양에 정의 된 기능적 인터페이스 임을 나타 내기 위해 사용되는 정보 주석 유형 입니다.

그리고 유스 케이스

기능적 인터페이스의 인스턴스는 람다 식, 메서드 참조 또는 생성자 참조를 사용하여 만들 수 있습니다.

일반적으로 다른 사용 사례를 배제하지 않는 표현. 주요 목적은 기능적 인터페이스 를 나타내는 것이기 때문에 실제 질문 은 " 람다 식 및 메소드 / 생성자 참조 이외의 기능적 인터페이스에 대한 다른 사용 사례가 있습니까?"로 요약됩니다.

때문에 기능적인 인터페이스는 Java 언어 사양에 의해 정의 된 자바 언어 구조이며, 규격은 그 질문에 대답 할 수있는 것만 :

JLS §9.8. 기능성 인터페이스 :

클래스를 선언하고 인스턴스화 (§15.9)하여 인터페이스 인스턴스를 작성하는 일반적인 프로세스 외에도 메소드 참조 표현식 및 람다 표현식 (§15.13, §15.27)으로 기능 인터페이스 인스턴스를 작성할 수 있습니다.

따라서 Java 언어 사양에서는 달리 언급하지 않습니다. 해당 섹션에 언급 된 유일한 사용 사례는 메소드 참조 표현식 및 람다 표현식으로 인터페이스 인스턴스를 작성하는 것입니다. (이것은 스펙에서 메소드 참조 표현식의 한 형식으로 언급되는 생성자 참조를 포함합니다).

따라서 한 문장에서 Java 8에는 다른 유스 케이스가 없습니다.


단지 너무 많거나 관련이없는 것을 요구할 수도 있지만 (응답하지 않기로 선택할 수 있음), 누군가가 유틸리티를 만들었을 때보 다 public static String generateTaskId()"기능적"이 public class TaskIdSupplier implements Supplier<String>되도록하는 get방법 은 무엇입니까? 기존 세대 구현. 기능 인터페이스의 오용, 특히 SupplierJDK 내장의 재사용이 있습니까? 추신 : 나는 이것을 물을 더 좋은 곳 / Q & A를 찾을 수 없었습니다. 제안 할 수 있다면 이주해 드리겠습니다.
나만

1
@Naman 명명 된 클래스를 만들 때 유틸리티 메소드를 더 기능적으로 만들지 않습니다 TaskIdSupplier. 이제 질문은 명명 된 클래스를 만든 이유입니다. 를 통해 구현 찾기를 지원하려는 경우와 같이 명명 된 유형이 필요한 시나리오가 있습니다 ServiceLoader. Supplier그때 구현하는 데 아무런 문제가 없습니다 . 그러나 필요하지 않으면 만들지 마십시오. 이 필요한 경우 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). 람다 식 / 메서드 참조를 통해 또는 수동으로 생성 된 클래스를 통해 함수를 표현하기 위해 클래스 유형을 사용하는 것은 Java에서 진정한 함수 유형이없는 경우 함수를 전달 하는 수단 일뿐 입니다. 필요한 경우에만 수행해야합니다.
Holger

1
작은 코드 조각 만 전달되는 경우이를 캡슐화하는 람다 식을 만들 수 있습니다. 메소드처럼 메소드를 호출해야 할 때마다 (코드 조각이 사소하지 않은 경우 테스트가 필요한 시나리오 포함) 호출 할 수있는 이름 지정된 메소드를 작성하고 메소드 참조 또는 람다 표현식 / 명시 적 클래스를 사용하십시오. 필요할 때 전달하기 위해 전화를 캡슐화합니다. 상수는 코드에 포함 된 람다 식 또는 메서드 참조의 효율성을 신뢰할 수없는 경우에만 유용합니다. 즉, 거의 필요하지 않습니다.
Holger

12

다른 사람들이 말했듯이 기능적 인터페이스는 한 가지 방법을 노출시키는 인터페이스입니다. 메소드가 둘 이상있을 수 있지만 다른 모든 메소드에는 기본 구현이 있어야합니다. 그것이 "기능적 인터페이스"라고 불리는 이유는 그것이 효과적으로 기능으로 작용하기 때문입니다. 인터페이스를 매개 변수로 전달할 수 있기 때문에 함수형 함수형 언어와 같이 함수가 이제 "일류 시민"임을 의미합니다. 여기에는 많은 이점이 있으며 Stream API를 사용할 때 많은 이점이 있습니다. 물론 람다식이 주된 용도입니다.


10

전혀. Lambda 표현식은 해당 주석의 유일한 포인트입니다.


6
lamdbas는 주석없이 작동합니다. 그것은 @Override컴파일러에게 "기능적"인 것을 작성하려고한다는 것을 알리는 것과 같은 주장 이다.
Thilo

1
조금 짧지 만 요점과 정답으로 곧장갑니다. 더 많은 단어로 같은 것을 말하는 더 정교한 대답 을 추가하는 데 시간이 걸렸습니다 …
Holger

5

람다 식은 기능적 인터페이스 유형에 할당 할 수 있지만 메소드 참조 및 익명 클래스도 지정할 수 있습니다.

의 특정 기능 인터페이스에 대한 하나의 좋은 점은 java.util.function그들이 (같은 새로운 기능을 만들기 위해 구성 될 수 있다는 것입니다 Function.andThenFunction.compose, Predicate.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 <>.

기능적 인터페이스의 응용 :

  • 메소드 참조
  • 람다 식
  • 생성자 참조

기능적 인터페이스를 배우고, 인터페이스의 첫 번째 기본 메소드를 배우고, 기능적 인터페이스를 학습 한 후에는 메소드 참조 및 람다 표현을 이해하기가 쉬울 것입니다


처음 두 예제에 '추상'키워드가 있어야합니까?
sofs1

1
@ sofs1 인터페이스에 선언 된 메소드는 기본적으로 public과 abstract입니다. 추상 클래스의 메소드 인 경우 추상 키워드를 사용해야합니다. 그러나 인터페이스에서도 메소드에 추상 키워드를 사용하는 것이 좋습니다. 이전 Java 버전과의 호환성을 위해 허용했지만 권장하지 않습니다.
Ketan

2

Java 8에서 람다를 사용할 수 있습니다

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 LambdasFunctionalInterfaces 에 대한 추가 정보


1

@FunctionalInterface 는 Java 8과 함께 릴리스 된 새로운 주석이며 람다 식의 대상 유형을 제공하며 코드 컴파일 시간 검사에 사용됩니다.

사용하고 싶을 때 :

1- 인터페이스에 둘 이상의 추상 메소드가 없어야합니다 . 그렇지 않으면 컴파일 오류가 발생합니다.

1 귀하의 인터페이스가 있어야 기능 인터페이스는 비 저장 클래스에 의해 구현하기위한 것입니다 즉, 순수, 순수의 부여 됨으로써이다 Comparator이 경우, 구현 상태에 의존하지 그것 때문에 인터페이스 가 없습니다 받게됩니다 컴파일 오류가 있지만, 많은 경우에 당신 이런 종류의 인터페이스에는 람다를 사용할 수 없습니다

java.util.function패키지는 다양한 범용 인터페이스 기능 등이 포함되어 Predicate, Consumer, Function, 및 Supplier.

또한이 주석없이 람다를 사용할 수 있습니다.


1

다른 답변 외에도 "람다 식과 직접 다른 기능 인터페이스를 사용하는 이유"의 주된 이유는 객체 지향 Java 언어의 특성과 관련이있을 수 있다고 생각합니다.

Lambda 표현식의 주요 속성은 다음과 같습니다. 1. 전달 될 수 있으며 2. 특정 시간 (몇 시간)에 실행될 수 있습니다. 이제 언어로이 기능을 지원하기 위해 일부 다른 언어는이 문제를 간단하게 처리합니다.

예를 들어 Java 스크립트에서 함수 (익명 함수 또는 함수 리터럴)는 객체로 처리 될 수 있습니다. 따라서 간단하게 만들 수도 있고 변수에 할당 할 수도 있습니다. 예를 들면 다음과 같습니다.

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

또는 ES6을 통해 화살표 기능을 사용할 수 있습니다.

const myFunction = ... => ...

지금까지 Java 언어 디자이너는 이러한 방식 (기능적 프로그래밍 기술)을 통해 언급 된 기능을 처리하는 것을 허용하지 않았습니다. 그들은 Java 언어가 객체 지향이라고 믿기 때문에 객체 지향 기술을 통해이 문제를 해결해야합니다. 그들은 Java 언어의 단순성과 일관성을 놓치고 싶지 않습니다.

따라서 인터페이스가 하나의 메소드 (기능적 인터페이스를 의미 함) 만있는 인터페이스의 객체가 필요할 때 인터페이스를 사용합니다. 람다 식으로 대체 할 수 있습니다. 같은 :

ActionListener listener = event -> ...;
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.