Stream (). Map (…) lambda ifadeleriyle nasıl hata ayıklanır?


115

Projemizde java 8'e geçiş yapıyoruz ve yeni özelliklerini test ediyoruz.

Projemde ben filtrelemek ve kullanarak bazı koleksiyonları dönüştürmek için Guava yüklemleri ve işlevleri kullanıyorum Collections2.transformve Collections2.filter.

Bu geçişte örneğin guava kodunu java 8'e değiştirmem gerekiyor. Yani, yaptığım değişiklikler şu türden:

List<Integer> naturals = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10,11,12,13);

Function <Integer, Integer> duplicate = new Function<Integer, Integer>(){
    @Override
    public Integer apply(Integer n)
    {
        return n * 2;
    }
};

Collection result = Collections2.transform(naturals, duplicate);

To ...

List<Integer> result2 = naturals.stream()
    .map(n -> n * 2)
    .collect(Collectors.toList());

Guava kullanarak, her dönüştürme işleminde hata ayıklayabildiğim için kodda hata ayıklarken çok rahattım, ancak mesela benim endişem nasıl hata ayıklayacağımdır .map(n -> n*2).

Hata ayıklayıcıyı kullanarak aşağıdaki gibi bazı kodlar görebilirim:

@Hidden
@DontInline
/** Interpretively invoke this form on the given arguments. */
Object interpretWithArguments(Object... argumentValues) throws Throwable {
    if (TRACE_INTERPRETER)
        return interpretWithArgumentsTracing(argumentValues);
    checkInvocationCounter();
    assert(arityCheck(argumentValues));
    Object[] values = Arrays.copyOf(argumentValues, names.length);
    for (int i = argumentValues.length; i < values.length; i++) {
        values[i] = interpretName(names[i], values);
    }
    return (result < 0) ? null : values[result];
}

Ama kodda hata ayıklamak Guava kadar doğru değil, aslında n * 2dönüşümü bulamadım .

Bu dönüşümü görmenin bir yolu veya bu kodda hata ayıklamanın kolay bir yolu var mı?

DÜZENLEME: Farklı yorumlardan cevap ekledim ve cevapları gönderdim

HolgerSoruma cevap veren yorum sayesinde , lambda bloğuna sahip olma yaklaşımı, dönüşüm sürecini görmeme ve lambda gövdesi içinde olanları ayıklamama izin verdi:

.map(
    n -> {
        Integer nr = n * 2;
        return nr;
    }
)

Stuart MarksYöntem referanslarına sahip olma yaklaşımı sayesinde , dönüşüm sürecindeki hataları da ayıklamamı sağladı:

static int timesTwo(int n) {
    Integer result = n * 2;
    return result;
}
...
List<Integer> result2 = naturals.stream()
    .map(Java8Test::timesTwo)
    .collect(Collectors.toList());
...

Marlon BernardesCevap sayesinde Eclipse'imin ne yapması gerektiğini göstermediğini ve peek () kullanımının sonuçların görüntülenmesine yardımcı olduğunu fark ettim.


Geçici resultdeğişkeninizi olarak bildirmenize gerek yoktur Integer. Basit inteğer yanı yapmalıyım mapping bir intbir etmek int...
Holger

Ayrıca IntelliJ IDEA 14'te geliştirilmiş hata ayıklayıcı olduğunu da ekledim. Artık Lamdas'da hata ayıklayabiliriz.
Mikhail

Yanıtlar:


86

Eclipse veya IntelliJ IDEA kullanırken genellikle lambda ifadelerinde hata ayıklama konusunda sorun yaşamıyorum. Sadece bir kesme noktası ayarlayın ve lambda ifadesinin tamamını incelemediğinizden emin olun (yalnızca lambda gövdesini inceleyin).

Lambdalarda Hata Ayıklama

Diğer bir yaklaşım, peekakışın unsurlarını incelemek için kullanmaktır :

List<Integer> naturals = Arrays.asList(1,2,3,4,5,6,7,8,9,10,11,12,13);
naturals.stream()
    .map(n -> n * 2)
    .peek(System.out::println)
    .collect(Collectors.toList());

GÜNCELLEME:

Sanırım kafanız karışıyor çünkü mapbir intermediate operation- başka bir deyişle: bu, yalnızca a terminal operationyürütüldükten sonra yürütülecek olan tembel bir işlem . Yani stream.map(n -> n * 2)lambda'yı aradığınızda vücut şu anda idam edilmiyor. Bir kesme noktası ayarlamanız ve bir terminal işlemi çağrıldıktan sonra incelemeniz gerekir ( collectbu durumda).

Kontrol Akış Operasyonu Diğer açıklamalar için.

GÜNCELLEME 2:

Aktaran Holger'in yorumunu:

Burada işi zorlaştıran şey, eşleme çağrısı ve lambda ifadesinin tek satırda olmasıdır, bu nedenle bir satır kesme noktası tamamen ilgisiz iki eylemde duracaktır.

Hemen map( arkasına bir satır sonu eklemek , yalnızca lambda ifadesi için bir kesme noktası belirlemenize izin verir. Ve hata ayıklayıcıların bir returnifadenin ara değerlerini göstermemesi olağandışı değildir . Lambda'yı olarak değiştirmek, n -> { int result=n * 2; return result; } sonucu incelemenizi sağlar. Yine, satır satır adım adım ilerlerken satır sonlarını uygun şekilde ekleyin…


Baskı ekranı için teşekkürler. Eclipse'in hangi sürümüne sahipsiniz veya bu iletişim kutusunu almak için ne yaptınız? Ben kullanarak çalıştı inspectve display ve almak n cannot be resolved to a variable. Btw, peek de kullanışlıdır ancak tüm değerleri bir kerede yazdırır. Dönüşümü kontrol etmek için her bir yineleme sürecini görmek istiyorum. Mümkün mü?
Federico Piazza

Eclipse Kepler SR2 kullanıyorum (Eclipse'in pazarından Java 8 desteği yüklenmiş).
Marlon Bernardes

Eclipse de kullanıyor musun? Sadece .mapsatırda bir kesme noktası ayarlayın ve F8'e birkaç kez basın.
Marlon Bernardes

6
@Fede: Burada zor olan şey, çağrının mapve lambda ifadesinin tek satırda olmasıdır, bu nedenle bir satır kesme noktası tamamen ilgisiz iki eylemde duracaktır. Hemen map(arkasına bir satır sonu eklemek , yalnızca lambda ifadesi için bir kesme noktası belirlemenize izin verir. Ve hata ayıklayıcıların bir returnifadenin ara değerlerini göstermemesi alışılmadık bir durum değildir . Lambda'yı olarak değiştirmek, n -> { int result=n * 2; return result; }incelemenizi sağlar result. Yine, satır satır adım atarken satır sonlarını uygun şekilde ekleyin…
Holger

1
@Marlon Bernardes: Tabii, cevaba ekleyebilirsiniz, çünkü yorumların amacı budur: içeriği iyileştirmeye yardımcı olmak. Btw., Alıntılanan metni kod formatlama ekleyerek düzenledim…
Holger

33

IntelliJ, Java Stream Debugger eklentisi gibi bu durum için çok güzel bir eklentiye sahiptir. Kontrol etmelisiniz: https://plugins.jetbrains.com/plugin/9696-java-stream-debugger?platform=hootsuite

IDEA Hata Ayıklayıcı aracı penceresini, Hata ayıklayıcı bir Akış API çağrıları zincirinin içinde durduğunda etkin hale gelen Mevcut Akış Zincirini İzle düğmesini ekleyerek genişletir.

Ayrı akış işlemleriyle çalışmak için güzel bir arayüze sahiptir ve size hata ayıklamanız gereken bazı değerleri izleme fırsatı verir.

Java Akışı Hata Ayıklayıcısı

Buraya tıklayarak Debug penceresinden manuel olarak başlatabilirsiniz:

görüntü açıklamasını buraya girin


23

Lambdalarda hata ayıklama, NetBeans ile de iyi çalışır. NetBeans 8 ve JDK 8u5 kullanıyorum.

Lambda bulunan bir satırda bir kesme noktası ayarlarsanız, aslında boru hattı kurulduğunda bir kez ve ardından her akış öğesi için bir kez vurursunuz. Örneğinizi kullanarak, kesme noktasına ilk ulaştığınızda map()akış ardışık düzenini ayarlayan çağrı olacaktır :

ilk kesme noktası

mainBeklediğiniz gibi çağrı yığınını ve yerel değişkenleri ve parametre değerlerini görebilirsiniz . Adım atmaya devam ederseniz, "aynı" kesme noktasına yeniden vurulur, ancak bu sefer lambda çağrısı dahilindedir:

görüntü açıklamasını buraya girin

Bu sefer çağrı yığınının akış makinesinin derinliklerinde olduğunu ve yerel değişkenlerin çevreleme mainyöntemi değil, lambda'nın kendisinin yerelleri olduğunu unutmayın . (Bunu naturalsnetleştirmek için listedeki değerleri değiştirdim .)

Marlon Bernardes'ın (+1) işaret ettiği gibi , peekdeğerleri boru hattında ilerledikçe incelemek için kullanabilirsiniz . Bunu paralel bir akıştan kullanıyorsanız dikkatli olun. Değerler, farklı iş parçacıkları arasında tahmin edilemeyen bir sırada yazdırılabilir. Değerleri bir hata ayıklama veri yapısında depoluyorsanız peek, bu veri yapısı elbette iş parçacığı açısından güvenli olmalıdır.

Son olarak, çok sayıda lambdada hata ayıklama yapıyorsanız (özellikle çok satırlı ifade lambdaları), lambda'yı adlandırılmış bir yönteme ayıklamak ve ardından bir yöntem referansı kullanarak buna başvurmak tercih edilebilir. Örneğin,

static int timesTwo(int n) {
    return n * 2;
}

public static void main(String[] args) {
    List<Integer> naturals = Arrays.asList(3247,92837,123);
    List<Integer> result =
        naturals.stream()
            .map(DebugLambda::timesTwo)
            .collect(toList());
}

Bu, siz hata ayıklarken neler olup bittiğini görmenizi kolaylaştırabilir. Ek olarak, yöntemlerin bu şekilde çıkarılması birim testini kolaylaştırır. Lambda'nız o kadar karmaşıksa, tek adımda geçmeniz gerekiyorsa, muhtemelen bunun için bir sürü birim testi yaptırmak istersiniz.


Benim sorunum, lambda gövdesinde hata ayıklayamamamdı, ancak yöntem referanslarını kullanma yaklaşımınız, istediğim şeyde bana çok yardımcı oldu. Cevabınızı { int result=n * 2; return result; }, farklı satırlar ekleyerek de mükemmel şekilde çalışan Holger yaklaşımını kullanarak güncelleyebilirsiniz ve her iki cevap da yardımcı olduğu için cevabı kabul edebilirim. Elbette +1.
Federico Piazza

1
@Fede Diğer cevap zaten güncellenmiş gibi görünüyor, bu yüzden benimkini güncellemenize gerek yok. Zaten çok çizgili lambdalardan nefret ediyorum. :-)
Stuart Marks

1
@Stuart Marks: Ben de tek satırlık lambdaları tercih ederim. Bu nedenle, genellikle - diğer (sıradan) bileşik ifadeler için de geçerli olan - hata ayıklamadan sonra satır sonlarını kaldırırım.
Holger

1
@Fede Endişelenme. Soru soran kişi olarak, hangi cevabı tercih ederseniz edin kabul etmek sizin hakkınızdır. +1 için teşekkürler.
Stuart Marks

1
Metot referansları oluşturmanın, metotları birim testleri için kolaylaştırmasının yanı sıra daha okunaklı bir kod yaptığını düşünüyorum. Mükemmel cevap! (+1)
Marlon Bernardes


6

Daha güncel ayrıntılar sağlamak için (Ekim 2019), IntelliJ, bu tür kod hatalarını ayıklamak için son derece kullanışlı olan oldukça güzel bir entegrasyon ekledi.

Lambda içeren bir satırda durduğumuzda, tuşuna F7basarsak (içeri adım atarsak) IntelliJ, hata ayıklanacak parçacığın ne olacağını vurgulayacaktır. Hangi yığınla hata ayıklayacağımızı değiştirebiliriz Tabve karar verdikten sonra F7tekrar tıklarız.

İşte açıklamak için bazı ekran görüntüleri:

1- F7(İçeri adım) tuşuna basın , vurguları (veya seçim modunu) gösterecektir. görüntü açıklamasını buraya girin

2- TabHata ayıklanacak parçacığı seçmek için birden çok kez kullanıngörüntü açıklamasını buraya girin

3- F7İçeri girmek için (adım at) tuşuna basın . görüntü açıklamasını buraya girin


1

IDE'leri kullanarak hata ayıklama her zaman yararlıdır, ancak bir akıştaki her öğede hata ayıklamanın ideal yolu, Java Steams tembel olarak değerlendirildiğinden, bir terminal yöntemi işleminden önce peek () kullanmaktır; bu nedenle, bir terminal yöntemi çağrılmadıkça, ilgili akış değerlendirilmez.

List<Integer> numFromZeroToTen = Arrays.asList(1,2,3,4,5,6,7,8,9,10);

    numFromZeroToTen.stream()
        .map(n -> n * 2)
        .peek(n -> System.out.println(n))
        .collect(Collectors.toList());
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.