Callable <T> ve Java 8'in Tedarikçi <T> arasındaki fark nedir?


14

CodeReview bazı üzerinden bazı öneriler sonra C # Java geçiş yapıyorum. Yani, LWJGL içine bakarken, hatırladığım bir şey, her çağrı yöntemin çağrıldığı Displayaynı iş parçacığı üzerinde yürütülmesi gerektiğiydi Display.create(). Bunu hatırlayarak biraz benzeyen bir sınıf çırptım.

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());
    }
}

Bu sınıfı yazarken, a isClosed()döndüren bir yöntem oluşturduğumu fark edeceksiniz Future<Boolean>. Bu harekât benim için bir işlev Schedulerhiçbir şey bir etrafında bir sarıcı daha fazladır arayüzüne ( ScheduledExecutorService. Yazarken scheduleüzerinde yöntemini Schedulerben de bir kullanabileceği fark I Supplier<T>argüman veya bir Callable<T>geçirilen işlevi temsil etmek argüman. ScheduledExecutorServiceBir içermiyordu için geçersiz kılın, Supplier<T>ancak lambda ifadesinin () -> Display.isCloseRequested()aslında hem Callable<bool> ve ile uyumlu olduğunu fark ettim Supplier<bool>.

Benim sorum şu, bu ikisi arasında anlamsal veya başka bir fark var mı?


Ben çalışmıyor = SO, işe yarayan ama gözden geçirilmesi gereken kod = CodeReview, kod veya programcı gerekebilir veya gerekmeyebilir genel sorular izlenim kodu altında idi. Kodum gerçekten işe yarıyor ve sadece örnek olarak var. Ben de gözden geçirme istemiyorum, sadece semantik hakkında soruyorum.
Dan Pantry

..bir şeyin anlambilimini sormak kavramsal bir soru değil mi?
Dan Pantry

Bence bu kavramsal bir soru, bu sitedeki diğer iyi sorular kadar kavramsal değil ama uygulama ile ilgili değil. Kod çalışıyor, soru kodla ilgili değil. Soru "bu iki arayüz arasındaki fark nedir?"

Neden C # 'dan Java' ya geçmek istersiniz!
Didier A.

2
Bir fark vardır, yani Callable.call () istisnalar atar ve Supplier.get () yapmaz. Bu, ikincisini lambda ifadelerinde çok daha çekici hale getirir.
Thorbjørn Ravn Andersen

Yanıtlar:


6

Kısa cevap, her ikisinin de fonksiyonel arayüzler kullanmasıdır, ancak tüm fonksiyonel arayüzlerin @FunctionalInterfaceek açıklama içermemesi gerektiğini de belirtmek gerekir . JavaDoc'un kritik kısmı şöyledir:

Ancak derleyici, arabirim bildiriminde bir FunctionalInterface ek açıklaması bulunup bulunmamasına bakılmaksızın, işlevsel bir arabirimin tanımını karşılayan herhangi bir arabirimi işlevsel bir arabirim olarak ele alır.

Ve fonksiyonel bir arayüzün en basit tanımı (basitçe, diğer istisnalar olmadan) sadece:

Kavramsal olarak, fonksiyonel bir arayüzün tam olarak bir soyut yöntemi vardır.

Bu nedenle, @Maciej Chalapuk'un cevabında, notu bırakmak ve istenen lambda'yı belirtmek de mümkündür:

// 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

Şimdi, hem yapar Callableve Supplieronlar tam olarak bir soyut yöntem içerirler çünkü fonksiyonel arayüzler geçerli:

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

Her iki yöntem de bir argüman almadığından ( MyInterface.myCall(int)örneğin aksine ), resmi parametreler boştur ( ()).

Lambda ifadesinin () -> Display.isCloseRequested()aslında hem Callable<Boolean> ve hem de tipiyle uyumlu olduğunu fark ettim Supplier<Boolean>.

Şimdiye kadar çıkarım yapabilmeniz gerektiği için, bunun nedeni her iki soyut yöntemin de kullandığınız ifadenin türünü döndürmesidir. Kesinlikle Callablea ScheduledExecutorService.

Daha fazla araştırma (sorunun kapsamı dışında)

Her iki arabirim de tamamen farklı paketlerden gelir , bu nedenle de farklı şekilde kullanılırlar. Sizin durumunuzda, Supplier<T>aşağıdakileri sağlamadığı sürece bir uygulamasının nasıl kullanılacağını göremiyorum Callable:

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

Birincisi () ->gevşekçe "a Supplierverir ..." ve ikincisi "a Callableverir ..." olarak yorumlanabilir. return value;gövdesidir Callablekendisi gövdesidir lambda, Supplierlambda.

Şimdi gerek Ancak, bu yapmacık örnekte kullanım hafifçe karmaşık bir hal alıyor get()dan Supplierönce ilk get()sonucunuzu -ting Futureteslim edecek olan call()Şu Verilerinizi Callableuyumsuz.

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

1
Bu cevaba kabul edilen cevabı değiştiriyorum çünkü bu sadece çok daha kapsamlı
Dan Pantry

Daha uzun, daha kullanışlı değildir; @ srrm_lwn'nin cevabına bakınız.
SensorSmith

@SensorSmith srrms yanıtı, kabul edilen cevap olarak işaretlediğim orijinal yanıttı. Bunun daha faydalı olduğunu düşünüyorum.
Dan Pantry

21

2 arayüz arasındaki temel farklardan biri, Callable'ın kontrol edilen istisnaların, bu uygulamanın içinden atılmasına izin verirken Tedarikçi bunu yapmamasıdır.

İşte JDK'dan kod parçacıkları bunu vurgulayan -

@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();
}

Bu Callable fonksiyonunu fonksiyonel arayüzlerde argüman olarak kullanılamaz hale getirir.
Basilevs

3
@Basilevs hayır değil - sadece Supplierakış API gibi bir beklenen yerlerde kullanılamaz . Kesinlikle lambdas ve yöntem referansları a Callable.
dimo414

13

Belirttiğiniz gibi, pratikte aynı şeyi yaparlar (bir çeşit değer sağlarlar), ancak prensipte farklı şeyler yapmayı amaçlamaktadırlar :

A Callable, " bir sonuç döndüren bir görevdir , a Supplierise" bir sonuç sağlayıcısı " Callabledır Supplier.

A Callableçok az iş yapabilir ve sadece bir değer döndürebilir. Ayrıca Supplieroldukça fazla iş yapmak da mümkündür (örn. Büyük bir veri yapısı oluşturmak). Ama genel olarak her ikisiyle de önemsediğiniz şeyleri konuşmak onların temel amacıdır. Örneğin ExecutorService, Callables ile çalışmak , çünkü asıl amacı iş birimlerini yürütmektir . Tembel olarak yüklenen bir veri deposu Supplier, ne kadar iş gerektirebileceğine dair çok fazla endişe duymadan bir değer sağlanmasına önem verdiği için a kullanır.

Ayrımı ifade etmenin başka bir yolu, a'nın Callableyan etkilere sahip olabilmesidir (örneğin bir dosyaya yazma), ancak a Suppliergenellikle yan etkisiz olmalıdır. Belgelerde açıkça belirtilmiyor (bir gereklilik olmadığı için ), ancak bu terimlerle düşünmenizi öneririm. Eğer çalışma idempotent ise a kullanın Supplier, değilse a kullanın Callable.


2

Her ikisi de özel anlambilimsiz normal Java arayüzleridir. Callable , eşzamanlı API'nın bir parçasıdır. Tedarikçi , yeni fonksiyonel programlama API'sının bir parçasıdır. Java8'deki değişiklikler sayesinde lambda ifadelerinden oluşturulabilir. @FunctionalInterface , derleyicinin arabirimin işlevsel olduğunu kontrol etmesini sağlar ve eğer değilse bir hata oluşturur, ancak bir arabirimin işlevsel bir arabirim olması ve lambdas tarafından uygulanması için bu ek açıklamaya gerek yoktur. Bu, bir yöntemin @Override olarak işaretlenmeden nasıl geçersiz kılınabileceği gibi, tam tersi olamaz.

Lambdalarla uyumlu kendi arayüzlerinizi tanımlayabilir ve @FunctionalInterfaceek açıklama ile belgeleyebilirsiniz . Belgeleme isteğe bağlıdır.

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

...

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

Her ne kadar bu özel arayüz IntPredicateJava'da denir .
Konrad Borowski
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.