Soru Java 8 Akışları ve kaynakları deneyin


Akış API'sının, kodu daha kolay okunması için burada olduğunu düşündüm. Oldukça sinir bozucu bir şey buldum. Stream arayüz (java.util.stream.Stream) genişletir AutoClosable arayüz (java.lang.AutoCloseable)

Dolayısıyla, akışlarınızı doğru bir şekilde kapatmak istiyorsanız, kaynakları kullanmayı deneyin.

Liste 1. Çok hoş değil, akarsular kapalı değil.

   public void noTryWithResource() {
    Set<Integer> photos = new HashSet<Integer>(Arrays.asList(1, 2, 3));

   @SuppressWarnings("resource") List<ImageView> collect = photos.stream()
            .map(photo -> new ImageView(new Image(String.valueOf(photo)))).collect(Collectors.<ImageView>toList());
}

Liste 2. 2 imbricated ile deneyin :(

   public void tryWithResource() {
    Set<Integer> photos = new HashSet<Integer>(Arrays.asList(1, 2, 3));

    try (Stream<Integer> stream = photos.stream()) {
        try (Stream<ImageView> map = stream
                .map(photo -> new ImageView(new Image(String.valueOf(photo))))) {
            List<ImageView> collect = map.collect(Collectors.<ImageView>toList());
        }
    }
}

Liste 3. Gibi map her ikisi de bir akış döndürür stream() ve map() işlevler kapalı olmalıdır.

    public void tryWithResource2() {
    Set<Integer> photos = new HashSet<Integer>(Arrays.asList(1, 2, 3));

    try (Stream<Integer> stream = photos.stream();
         Stream<ImageView> map = stream
                 .map(photo -> new ImageView(new Image(String.valueOf(photo))))) {
        List<ImageView> collect = map.collect(Collectors.<ImageView>toList());
    }
}

Verdiğim örnek bir anlam ifade etmiyor. Ben değiştirdim Path görüntüleri ile jpg Integer, örnek uğruna. Ama bu ayrıntılara dikkat etmene izin verme.

Otomatik kapanabilir akışlarla dolaşmanın en iyi yolu nedir? Gösterdiğim 3 seçenekten hiç memnun kalmadığımı söylemeliyim. Ne düşünüyorsun? Daha başka zarif çözümler var mı?


18
2017-09-11 20:12


Menşei


Denemede denemek için sözdizimsel bir nedeniniz yoktu. - Marko Topolnik
olası kopyası Neden Java 8 Stream sınıfı AutoCloseable? - David Limkys


Cevaplar:


Kullanıyorsun @SuppressWarnings("resource") Kapanmamış bir kaynak hakkında bir uyarıyı muhtemelen bastırır. Bu, yayınlanan uyarılardan biri değil javac. Web aramaları, Eclipse'nin uyarı verirse AutoCloseable kapatılmamış bırakılır.

Bu göre makul bir uyarıdır Java 7 özellikleri bu tanıtıldı AutoCloseable:

Artık gerekmediğinde kapatılması gereken bir kaynak.

Ancak Java 8 özellikleri için AutoCloseable "Kapalı olmalı" maddesini kaldırmak için rahattı. Şimdi, kısmen diyor ki

Kaynakları tutabilecek bir nesne ... kapanana kadar.

Alt sınıflarının veya örneklerinin tümü çıkarılabilir kaynakları olmamasına rağmen, AutoCloseable'ı uygulamak temel bir sınıf için mümkündür ve aslında yaygındır. Tam genellemede çalışması gereken ya da AutoCloseable örneğinin kaynak salıverilmesi gerektirdiği biliniyorsa, kaynaklarda denemelerin kullanılması önerilir. Ancak, G / Ç tabanlı ve G / Ç tabanlı formları destekleyen Akışı gibi özellikleri kullanırken, G / Ç tabanlı olmayan formları kullanırken kaynak blokları kullanmayı denemek gereksizdir.

Bu konu, Lambda uzman grubu içinde kapsamlı bir şekilde tartışıldı; bu mesaj kararı özetler. Diğer şeylerin yanı sıra AutoCloseable şartname (yukarıda anılan) ve BaseStream şartname (diğer cevaplar tarafından belirtilmiştir). Ayrıca, değiştirilen semantikler için Eclipse kod denetçisinin ayarlanması gerekliliğinden de bahsetmektedir, muhtemelen uyarıları koşulsuz olarak yayınlamamaktadır. AutoCloseable nesneler. Görünüşe göre bu mesaj Eclipse'e ulaşmadı ya da henüz değişmedi.

Özetle, Eclipse uyarıları sizi kapatmanız gerektiğini düşünürse AutoCloseable nesneler, bu yanlış. Sadece belirli belirli AutoCloseable nesnelerin kapatılması gerekiyor. Eclipse'in (henüz değilse) tüm uyarıları yaymamaları gerekiyor. AutoCloseable nesneler.


23
2017-09-15 04:21



Stuart Marks'ın yanı sıra Holger'ın açıklamanız için teşekkür ederiz. - fan


Akış sadece kendi kendini temizleme, genellikle I / O yapmak gerekiyorsa, akışları kapatmanız gerekir. Örneğiniz bir HashSet kullanıyor, bu yüzden kapatılmasına gerek yok.

itibaren Akış javadoc:

Genellikle, kaynağı bir IO kanalı olan akışlar (Files.lines (Yol, Karakter) tarafından döndürülenler gibi) kapanmayı gerektirir. Çoğu akış, özel kaynak yönetimi gerektirmeyen koleksiyonlar, diziler veya oluşturma işlevleri tarafından desteklenir.

Yani örneğinizde bu sorun olmadan çalışmalıdır

List<ImageView> collect = photos.stream()
                       .map(photo -> ...)
                       .collect(toList());

DÜZENLE

Kaynakları temizlemeniz gerekse bile, sadece bir try-with-resource kullanabilmeniz gerekir. Dosyadaki her satırın bir resmin yolunun olduğu bir dosya okuduğunuzu varsayalım:

 try(Stream<String> lines = Files.lines(file)){
       List<ImageView> collect = lines
                                  .map(line -> new ImageView( ImageIO.read(new File(line)))
                                  .collect(toList());
  }

10
2017-09-11 20:21



Bu benim amacım. Setin kapalı olması gerekmez. Ancak API, kapatılmasını gerektirir. - fan


“Kapatılabilir”, “kapatılabilir”, “kapatılmalıdır” anlamına gelir.

Bu geçmişte doğruydu, ör. görmek ByteArrayOutputStream:

Bir kapanış ByteArrayOutputStream etkisi yok.

Ve bu şu an için doğru Streams nerede belgeler açık hale getirir:

Akımların bir BaseStream.close() yöntem ve uygulama AutoCloseableAncak, neredeyse tüm akış örneklerinin kullanımdan sonra gerçekten kapatılması gerekmez. Genel olarak, kaynağı yalnızca IO kanalı olan akışlar (örneğin, Files.lines(Path, Charset)) kapanış gerektirecektir.

Dolayısıyla, bir denetim aracı yanlış uyarılar üretirse, bu, API'den değil, denetim aracıyla ilgili bir sorundur.

Kaynak yönetimi eklemek istiyorsanız bile, yuvaya gerek kalmayacağını unutmayın. try ifadeleri. Aşağıdakiler yeterli iken:

final Path p = Paths.get(System.getProperty("java.home"), "COPYRIGHT");
try(Stream<String> stream=Files.lines(p, StandardCharsets.ISO_8859_1)) {
    System.out.println(stream.filter(s->s.contains("Oracle")).count());
}

ikincil de ekleyebilirsiniz Stream ek bir kaynak olmadan kaynak yönetimine try:

final Path p = Paths.get(System.getProperty("java.home"), "COPYRIGHT");
try(Stream<String> stream=Files.lines(p, StandardCharsets.ISO_8859_1);
    Stream<String> filtered=stream.filter(s->s.contains("Oracle"))) {
    System.out.println(filtered.count());
}

7
2017-09-12 09:04





Kaynakla-try-resource-statement ile akışları güvenilir bir şekilde kapatan bir yardımcı program yöntemi oluşturmak mümkündür.

Bir deneme gibi bir nihayet bu bir ifade (örneğin, Scala örneğinde olduğu gibi).

/**
 * Applies a function to a resource and closes it afterwards.
 * @param sup Supplier of the resource that should be closed
 * @param op operation that should be performed on the resource before it is closed
 * @return The result of calling op.apply on the resource 
 */
private static <A extends AutoCloseable, B> B applyAndClose(Callable<A> sup, Function<A, B> op) {
    try (A res = sup.call()) {
        return op.apply(res);
    } catch (RuntimeException exc) {
        throw exc;
    } catch (Exception exc) {
        throw new RuntimeException("Wrapped in applyAndClose", exc);
    }
}

(Sık sık kapatılması gereken kaynaklar, çalışma zamanı dışındaki istisnalar ayrıldıklarında istisnalar da attığından, çalışma zamanı istisnalarına sarılırlar ve bunu yapan ayrı bir yönteme gerek kalmaz.)

Bu yöntemle, sorudaki örnek şu şekildedir:

Set<Integer> photos = new HashSet<Integer>(Arrays.asList(1, 2, 3));

List<ImageView> collect = applyAndClose(photos::stream, s -> s
    .map(photo -> new ImageView(new Image(String.valueOf(photo))))
    .collect(Collectors.toList()));

Bu, kullanım sırasında olduğu gibi, akış kapatmanın gerekli olduğu durumlarda yararlıdır. Files.lines. Ayrıca, örneğinizde olduğu gibi, "çifte kapanma" yapmanız gerektiğinde de yardımcı olur Liste 3.

Bu cevap bir uyarlamanın eski cevap benzer bir soruya.


3
2018-01-26 11:28