Callable <T>과 Java 8의 Supplier <T>의 차이점은 무엇입니까?


14

CodeReview에서 일부 추천을받은 후 C #에서 Java로 전환했습니다. 따라서 LWJGL을 살펴볼 때 기억 Display해야 할 한 가지는 모든 호출 이 Display.create()메소드가 호출 된 동일한 스레드에서 실행되어야한다는 것 입니다. 이것을 기억하면서, 나는 이것과 비슷한 클래스를 만들었습니다.

public class LwjglDisplayWindow implements DisplayWindow {
    private final static int TargetFramesPerSecond = 60;
    private final Scheduler _scheduler;

    public LwjglDisplayWindow(Scheduler displayScheduler, DisplayMode displayMode) throws LWJGLException {
        _scheduler = displayScheduler;
        Display.setDisplayMode(displayMode);
        Display.create();
    }

    public void dispose() {
        Display.destroy();
    }

    @Override
    public int getTargetFramesPerSecond() { return TargetFramesPerSecond; }

    @Override
    public Future<Boolean> isClosed() {
        return _scheduler.schedule(() -> Display.isCloseRequested());
    }
}

이 클래스를 작성하는 동안 isClosed()을 반환하는 메소드를 작성했음을 알 수 Future<Boolean>있습니다. 이 파견 제에 기능 Scheduler것도이 래퍼보다 더 많은입니다 인터페이스 ( ScheduledExecutorService. 묘화 동안 schedule온 방법 Scheduler내가 중 하나 사용할 수있는 것으로 나타났습니다 I Supplier<T>인수 또는Callable<T> 에 전달되는 기능을 나타내는 인수하는 것은. ScheduledExecutorService를 포함하지 않았다 대한 오버라이드 (override) Supplier<T>하지만 난 람다 식을 것으로 나타났습니다 () -> Display.isCloseRequested()실제로 모두 호환 유형 Callable<bool> Supplier<bool> .

내 질문은 의미 상 또는 다른 방법으로 그 둘 사이에 차이가 있습니까? 그렇다면 그렇다면 무엇을 준수해야합니까?


작동하지 않는 노출 코드 = SO, 작동하지만 검토가 필요한 코드 = CodeReview, 코드 = 프로그래머가 필요하거나 필요하지 않은 일반적인 질문을 받았습니다. 내 코드는 실제로 작동하며 예제로만 존재합니다. 나는 의미론에 대해서만 묻는 리뷰를 요구하지 않습니다.
Dan Pantry

.. 무언가의 의미론을 묻는 것은 개념적 질문이 아닌가?
Dan Pantry

나는 이것이이 사이트의 다른 좋은 질문만큼 개념적이 아니라 개념적 질문이라고 생각하지만 그것은 구현에 관한 것이 아닙니다. 코드가 작동하고 문제는 코드에 관한 것이 아닙니다. 문제는 "이 두 인터페이스의 차이점은 무엇입니까?"입니다.

왜 C #에서 Java로 전환하고 싶습니까?
Didier A.

2
한 가지 차이점이 있습니다. 즉 Callable.call ()에서 예외가 발생하고 Supplier.get ()에서 예외가 발생하지 않습니다. 람다 식에서 후자를 훨씬 더 매력적으로 만듭니다.
Thorbjørn Ravn Andersen

답변:


6

짧은 대답은 둘 다 기능 인터페이스를 사용한다는 것입니다. 그러나 모든 기능 인터페이스에 @FunctionalInterface주석이 있어야하는 것은 아닙니다 . JavaDoc의 중요한 부분은 다음과 같습니다.

그러나 컴파일러는 FunctionalInterface 주석이 인터페이스 선언에 있는지 여부에 관계없이 기능 인터페이스의 정의를 충족하는 모든 인터페이스를 기능 인터페이스로 취급합니다.

그리고 기능적 인터페이스의 가장 간단한 정의는 다음과 같습니다.

개념적으로 기능 인터페이스에는 정확히 하나의 추상 메소드가 있습니다.

따라서 @Maciej Chalapuk의 답변에서 주석을 삭제하고 원하는 람다를 지정할 수도 있습니다.

// interface
public interface MyInterface {
    boolean myCall(int arg);
}

// method call
public boolean invokeMyCall(MyInterface arg) {
    return arg.myCall(0);
}

// usage
instance.invokeMyCall(a -> a != 0); // returns true if the argument supplied is not 0

이제 인터페이스 CallableSupplier기능 인터페이스 를 모두 만드는 것은 정확히 하나의 추상 메소드를 포함하기 때문입니다.

  • Callable.call()
  • Supplier.get()

두 방법 모두 인수를 사용하지 않으므로 ( MyInterface.myCall(int)예 와 반대 ) 공식 매개 변수는 비어 있습니다 ( ()).

나는 람다 표현식 것으로 나타났습니다 () -> Display.isCloseRequested()실제로 모두 호환 유형 Callable<Boolean> Supplier<Boolean> .

지금까지 유추 할 수 있듯이, 두 추상 메소드 모두 사용하는 표현식의 유형을 리턴하기 때문입니다. 당신은 확실히 Callable주어진 사용법을 사용해야 합니다 ScheduledExecutorService.

추가 탐구 (질문의 범위를 넘어서)

두 인터페이스는 완전히 다른 패키지 에서 제공되므로 다르게 사용됩니다. 귀하의 경우에는, 나는의 구현이 표시되지 않습니다 Supplier<T>그것은을 공급하지 않는 한, 사용됩니다 Callable:

public static <T> Supplier<Callable<T>> getCallable(T value) {
    return () -> () -> {
        return value;
    };
}

첫 번째 () ->는 "a Suppliergives ..." 로 느슨하게 해석되고 두 번째는 "a Callablegives ..." 로 해석 될 수 있습니다 . return value;의 본체 인 Callable자체의 본문 인 람다 Supplier람다.

당신이 지금 필요로하지만,이 인위적인 예에서는 사용이 약간 복잡해진다 get()로부터 Supplier전에 먼저 get()으로부터 당신의 결과를 -ting Future에 설정되는, call()당신을 Callable비동기 적으로.

public static <T> T doWork(Supplier<Callable<T>> callableSupplier) {
    // service being an instance of ExecutorService
    return service.submit(callableSupplier.get()).get();
}

1
이 단지 훨씬 더 포괄적이기 때문에 나는이 대답에 허용 대답을 전환하고있어
댄 식료품 저장실

더 오래 쓸수록 유용하지 않습니다. @srrm_lwn의 답변을 참조하십시오.
SensorSmith

@ SensorSmith srrms 답변은 내가 받아 들인 답변으로 표시된 원래 답변이었습니다. 나는 여전히 이것이 더 유용하다고 생각합니다.
Dan Pantry

21

두 인터페이스의 기본적인 차이점 중 하나는 Callable을 통해 구현 된 구현에서 확인 된 예외를 처리 할 수 ​​있지만 공급 업체는 그렇지 않습니다.

다음은 JDK의 코드 스 니펫입니다.

@FunctionalInterface
public interface Callable<V> {
/**
 * Computes a result, or throws an exception if unable to do so.
 *
 * @return computed result
 * @throws Exception if unable to compute a result
 */
V call() throws Exception;
}

@FunctionalInterface
public interface Supplier<T> {

/**
 * Gets a result.
 *
 * @return a result
 */
T get();
}

이것은 함수형 인터페이스에서 Callable을 인수로 사용할 수 없게 만듭니다.
Basilevs 2016 년

3
@Basilevs 아니오 그렇지 않습니다- Supplier스트림 API와 같이을 기대하는 곳에서는 사용할 수 없습니다 . 람다 및 메서드 참조를 a를 사용하는 메서드에 전달할 수 있습니다 Callable.
dimo414

13

참고로 실제로는 동일한 작업을 수행하지만 (일종의 가치 제공) 원칙적으로 다른 작업을 수행 하려고 합니다.

A Callable는 " 결과를 반환하는 작업 이고 a Supplier는" 결과의 공급 업체 "입니다. 즉 a Callable는 아직 실행되지 않은 작업 단위 Supplier를 참조하는 방법 이고, a 는 아직 알려지지 않은 값을 참조하는 방법입니다.

그것은이있을 가능성이 Callable아주 작은 일을 단순히 값을 반환 할 수있다. 또한 Supplier많은 작업을 수행 할 수도 있습니다 (예 : 큰 데이터 구조 구성). 그러나 일반적으로 당신이 관심을 갖는 것은 그들의 기본 목적입니다. 예를 들어, ExecutorService작업 단위 Callable실행 하는 것이 주된 목적이기 때문에 s 로 작업합니다. 지연로드 된 데이터 저장소는 소요되는 작업량에 대한 걱정없이 값을 제공 하는 Supplier것에 관심이 있기 때문에를 사용합니다 .

구별을 표현하는 또 다른 방법은 a Callable가 부작용 (예 : 파일에 쓰는 것)을 가질 수있는 반면에 a Supplier는 일반적으로 부작용 이 없어야한다는 것입니다. 설명서에는 이것을 명시 적으로 언급하지 않았지만 ( 필수 사항 이 아니기 때문에 ) 그런 용어로 생각하는 것이 좋습니다. 작업이 dem 등원 인 경우을 사용하고 Supplier그렇지 않은 경우을 사용하십시오 Callable.


2

둘 다 특별한 의미가없는 일반적인 Java 인터페이스입니다. 호출 가능 은 동시 API의 일부입니다. 공급 업체 는 새로운 기능적 프로그래밍 API의 일부입니다. Java8의 변경 덕분에 람다 식으로 만들 수 있습니다. @FunctionalInterface 는 컴파일러가 인터페이스가 작동하는지 확인하고 인터페이스가 작동하지 않으면 오류를 발생 시키지만 인터페이스는 해당 주석이 기능적 인터페이스 일 필요가없고 람다로 구현 될 필요가 없습니다. 이것은 메소드가 @Override로 표시되지 않고 오버라이드가 될 수있는 방법과 같습니다.

람다와 호환되는 고유 한 인터페이스를 정의하고 @FunctionalInterface주석으로 문서화 할 수 있습니다 . 문서화는 선택 사항입니다.

@FunctionalInterface
public interface MyInterface {
    boolean myCall(int arg);
}

...

MyInterface var = (int a) -> a != 0;

이 특정 인터페이스를 주목할 가치는 있지만 IntPredicateJava에서 호출 됩니다.
Konrad Borowski
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.