Java'daki bir goto ifadesine alternatif


84

Java'da goto anahtar sözcüğü için alternatif bir işlev nedir ?

Java'nın bir gotou olmadığı için.


10
@harigm: Eğer yazacağını kod tür bir örnek verebilir eğer goto Java kullanılabilir? Bence daha önemli olan burada yatıyor.
poligenel yağlayıcılar

3
@harigm: Ben ikinci poligene, lütfen sorunuzu programınızın neden bir gitmeye ihtiyacı olduğunu içerecek şekilde günceller misiniz?
Herkes

13
İnsanlar her zaman bir goto kullanmamaktan bahseder, ama bence oldukça iyi bilinen ve kullanılan gerçekten iyi bir gerçek dünya kullanım durumu var .. Yani, bir işlevden dönmeden önce bazı kodları çalıştırdığınızdan emin olun .. Genellikle yayınlanması kilitler ya da ne değil, ama benim durumumda, dönüşten hemen önce bir molaya atlayabilmeyi çok isterim, böylece gerekli zorunlu temizliği yapabilirim. Elbette, her yerde goto'larla dolu bir program korkunç olurdu, ancak yöntem bedenleriyle sınırlıysa, bir kongre izlediğiniz sürece o kadar da kötü görünmüyor (yalnızca işlevlerin sonuna atlayın, asla geri dönmeyin)
Matt Wolfe

7
İyi bir şirketsiniz. Linus Torvalds, goto kerneltrap.org/node/553'ü tutkuyla savundu . Kuşkusuz, Project Coin'deki en önemli eksiklik.
Fletch

7
@MattWolfe bu örnekteki işi sonunda denemiyor mu?
Andres Riofrio

Yanıtlar:


84

Etiketli bir BREAK ifadesi kullanabilirsiniz :

search:
    for (i = 0; i < arrayOfInts.length; i++) {
        for (j = 0; j < arrayOfInts[i].length; j++) {
            if (arrayOfInts[i][j] == searchfor) {
                foundIt = true;
                break search;
            }
        }
    }

Ancak, düzgün tasarlanmış kodda GOTO işlevine ihtiyacınız olmamalıdır.


89
Bu sizi raptorlardan kurtarmayacak!
Bobby

25
Doğru tasarlanmış kodda neden goto işlevine ihtiyacınız olmasın?
rogermushroom

13
Çünkü montaj, onu kullanmasına izin verilen tek kişi. Bir while ifadesi, bir for ifadesi, bir do-while, bir foreach, bir işlev çağrısı, bunların hepsi kontrollü ve öngörülebilir bir şekilde kullanılan GOTO ifadeleridir. Birleştirici düzeyinde, her zaman GOTO'lar olacaktır, ancak saf GOTO'nun size sağladığı işlevselliğe ihtiyacınız olmamalıdır - işlevlere ve döngü / kontrol ifadelerine göre tek avantajı, herhangi bir kodun ortasına, herhangi bir yere atlamanıza izin vermesidir. . Ve bu 'avantaj'ın kendisi de' spagetti kodu'nun tanımıdır.

1
@whatsthebeef Edsger W. Dijkstra'nın neden zararlı olduğunu düşündüğünü açıklayan 1968 tarihli ünlü mektubu .
thomanski

10
" Raptors " bağlamı , xkcd # 292 .
Peter Mortensen

42

gotoJava'da kavramın doğrudan bir eşdeğeri yoktur . Bir klasikle yapabileceğiniz bazı şeyleri yapmanıza izin veren birkaç yapı vardır goto.

  • breakVe continueifadeleri bir döngü veya switch ifadesinde bir blok atlamaya izin verir.
  • Etiketli bir ifade ve break <label>rasgele bir bileşik ifadeden belirli bir yöntem (veya başlatıcı bloğu) içindeki herhangi bir seviyeye atlamanıza izin verir.
  • Bir döngü ifadesini etiketlerseniz, şunları yapabilirsiniz: continue <label> , bir iç döngüden bir dış döngünün sonraki yinelemesine devam .
  • İstisnaları fırlatmak ve yakalamak, bir yöntem çağrısının birçok düzeyinden (etkili bir şekilde) çıkmanıza olanak tanır. (Bununla birlikte, istisnalar nispeten pahalıdır ve "sıradan" kontrol akışı yapmanın kötü bir yolu olarak kabul edilir 1. )
  • Ve tabii ki var return .

Bu Java yapılarının hiçbiri geriye doğru veya koddaki bir noktaya geçerli deyimle aynı iç içe geçme düzeyinde dallanmanıza izin vermez. Hepsi bir veya daha fazla yuvalama (kapsam) seviyesine continueatlarlar ve hepsi (dışında ) aşağıya doğru atlarlar. Bu kısıtlama, eski BASIC, FORTRAN ve COBOL kod 2'de bulunan "spagetti kodu" sendromunun önlenmesine yardımcı olur .


1- İstisnaların en pahalı kısmı, istisna nesnesinin ve yığın izlemesinin fiili olarak oluşturulmasıdır. Gerçekten, gerçekten "normal" akış kontrolü için istisna işlemeyi kullanmanız gerekiyorsa, istisna nesnesini önceden tahsis edebilir / yeniden kullanabilir veya fillInStackTrace()yöntemi geçersiz kılan özel bir istisna sınıfı oluşturabilirsiniz . Olumsuz tarafı, istisnanınprintStackTrace() yöntemlerinin, onları aramanız gerekmesi durumunda size yararlı bilgiler vermemesidir.

2 - Spagetti kodu sendromu, yapılandırılmış programlama yaklaşımını doğurdu , burada mevcut dil yapılarını kullanımınızı sınırlandırdınız. Bu BASIC , Fortran ve COBOL'a uygulanabilir , ancak özen ve disiplin gerektiriyordu. gotoTamamen kurtulmak pragmatik olarak daha iyi bir çözümdü. Bir dilde tutarsan, her zaman onu kötüye kullanacak bir palyaço vardır.


While (...) {} ve for (...) {} gibi yapıları döngülemek , rasgele bir konuma açıkça atlamadan kod bloklarını tekrar etmenize izin verir. Ayrıca myMethod () yöntem çağrıları, başka bir yerde bulunan kodu çalıştırmanıza ve bittiğinde geçerli bloğa dönmenize izin verir. Bunların tümü, goto'nun izin verdiği geri dönmeme gibi genel sorunu önlerken, goto işlevini değiştirmek için kullanılabilir .
Chris Nava

@Chris - bu doğru, ancak GOTO'yu destekleyen çoğu dilin Java döngü yapılarına daha yakın analogları da var.
Stephen C

1
@Chris - klasik FORTRAN'ın 'for' döngüleri vardır ve klasik COBOL'un döngü yapıları da vardır (ayrıntıları hatırlayamasam da). Yalnızca klasik BASIC'in açık döngü yapıları yoktur ...
Stephen C

31

Sadece eğlence için, işte Java'da bir GOTO uygulaması.

Misal:

   1 public class GotoDemo {
   2     public static void main(String[] args) {
   3         int i = 3;
   4         System.out.println(i);
   5         i = i - 1;
   6         if (i >= 0) {
   7             GotoFactory.getSharedInstance().getGoto().go(4);
   8         }
   9         
  10         try {
  11             System.out.print("Hell");
  12             if (Math.random() > 0) throw new Exception();            
  13             System.out.println("World!");
  14         } catch (Exception e) {
  15             System.out.print("o ");
  16             GotoFactory.getSharedInstance().getGoto().go(13);
  17         }
  18     }
  19 }

Çalıştırmak:

$ java -cp bin:asm-3.1.jar GotoClassLoader GotoDemo           
   3
   2
   1
   0
   Hello World!

"Kullanmayın!" Eklemem gerekir mi?


2
Hata: Math.random()0 döndürebilir.> =
poitroae

Evet güzel. Yalnızca bayt kodu korsanlığı tarafından uygulanabilen hiçbir şey gerçekten Java değildir ...
Stephen C

Satır numaraları yerine etiketleri destekleyen benzer bir şey var mı?
Behrouz.M

1
Bir uygulamaya giden bağlantı koptu.
LarsH

@poitroae Birinin HellWorld!o World!çıktı bulup şaşkına döneceği eğlenceli bir Paskalya yumurtası . #AllBugsAreFeatures
ReinstateMonica3167040

18

Bazı yorum yapanlar ve downvoters bu olmadığını iddia ederken git , üretilen bayt kodu Java ifadeleri aşağıda gerçekten bu ifadeler gerçekten ekspres yapmak olduğunu göstermektedir git anlambilim.

Spesifik olarak, do {...} while(true);ikinci örnekteki döngü, döngü koşulunu değerlendirmemek için Java derleyicileri tarafından optimize edilmiştir.

İleri atlamak

label: {
  // do stuff
  if (check) break label;
  // do more stuff
}

Bayt kodunda:

2  iload_1 [check]
3  ifeq 6          // Jumping forward
6  ..

Geri atlamak

label: do {
  // do stuff
  if (check) continue label;
  // do more stuff
  break label;
} while(true);

Bayt kodunda:

 2  iload_1 [check]
 3  ifeq 9
 6  goto 2          // Jumping backward
 9  ..

Nasıl yapılır / yaparken geriye doğru atlar? Hâlâ bloktan çıkıyor. Sayaç Örneği: label: do {System.out.println ("Geri"); devam etiketi;} while (yanlış); Sanırım sizin örneğinizde geri sıçramadan sorumlu olan while (doğru).
Ben Holland

@BenHolland: continue labelgeriye doğru sıçrama
Lukas Eder

Hâlâ ikna olmadım. Continue deyimi, for, while veya do-while döngüsünün geçerli yinelemesini atlar. Devam, döngünün gövdesinin sonuna atlar ve daha sonra döngüyü kontrol eden boole ifadesi için değerlendirilir. Süre geriye doğru sıçrayan, devam eden değil. Bkz. Docs.oracle.com/javase/tutorial/java/nutsandbolts/branch.html
Ben Holland

@BenHolland: Teknik olarak haklısın. Mantıksal bir perspektiften, eğer ilgi ifadeleri varsa // do stuffve öyleyse // do more stuff, geriye doğru atlama continue label "etkisine sahiptir"while(true) (elbette ifadeden dolayı ). Bunun bu kadar kesin bir soru olduğunun farkında değildim ...
Lukas Eder

2
@BenHolland: Cevabı güncelledim. while(true)Bir içine derleyici tarafından çevrilmiştir gotobaytkodu operasyonu. Gibi truesabit değişmez olduğu, derleyici bu optimizasyona yapabilir ve bir şey değerlendirmek zorunda değildir. Öyleyse, benim örneğim gerçekten bir goto, geriye atlamak ...
Lukas Eder

5

Gerçekten goto ifadeleri gibi bir şey istiyorsanız, her zaman adlandırılmış bloklara geçmeyi deneyebilirsiniz.

Etiketi kırmak için blok kapsamında olmanız gerekir:

namedBlock: {
  if (j==2) {
    // this will take you to the label above
    break namedBlock;
  }
}

Neden gitmemeniz gerektiği konusunda size ders vermeyeceğim - bunun cevabını zaten bildiğinizi varsayıyorum.


2
Bunu buraya bağlı SO sorumda uygulamayı denedim . Aslında sizi "yukarıdaki etikete" götürmüyor, belki yazarın ifadelerini yanlış yorumlamış olabilirim, ancak açıklık olması için sizi aşağıdaki dış bloğun ("etiketli" blok) sonuna götürdüğünü ekleyeceğim. nerede kırıyorsun. Dolayısıyla yukarı doğru "ayrılamazsınız". En azından benim anlayışım bu.
NameSpace

4
public class TestLabel {

    enum Label{LABEL1, LABEL2, LABEL3, LABEL4}

    /**
     * @param args
     */
    public static void main(String[] args) {

        Label label = Label.LABEL1;

        while(true) {
            switch(label){
                case LABEL1:
                    print(label);

                case LABEL2:
                    print(label);
                    label = Label.LABEL4;
                    continue;

                case LABEL3:
                    print(label);
                    label = Label.LABEL1;
                    break;

                case LABEL4:
                    print(label);
                    label = Label.LABEL3;
                    continue;
            }
            break;
        }
    }

    public final static void print(Label label){
        System.out.println(label);
    }

Bu, StackOverFlowException ile sonuçlanacak, yeterince uzun süre çalıştırmanız, bu nedenle gitmem gerek.
Kevin Parker

3
@Kevin: Bu nasıl bir yığın taşmasına yol açar? Bu algoritmada yineleme yok ...
Lukas Eder

1
Bu, herhangi bir programın "goto" kullanılmadan yazılabileceğinin orijinal kanıtı gibi görünüyor - onu herhangi bir gotodan on kat daha kötü olan bir "etiket" değişkeniyle bir while döngüsüne çevirerek.
gnasher729

3

StephenC şöyle yazıyor:

Klasik bir goto ile yapabileceğiniz bazı şeyleri yapmanızı sağlayan iki yapı vardır.

Bir tane daha...

Matt Wolfe şöyle yazıyor:

İnsanlar her zaman bir goto kullanmamaktan bahseder, ancak bence oldukça iyi bilinen ve kullanılan gerçekten iyi bir gerçek dünya kullanım durumu var .. Yani, bir işlevden dönmeden önce bazı kodları çalıştırdığınızdan emin olun .. Genellikle yayınlanması kilitler ya da ne değil, ama benim durumumda, dönüşten hemen önce bir molaya atlayabilmeyi çok isterim, böylece gerekli zorunlu temizliği yapabilirim.

try {
    // do stuff
    return result;  // or break, etc.
}
finally {
    // clean up before actually returning, even though the order looks wrong.
}

http://docs.oracle.com/javase/tutorial/essential/exceptions/finally.html

Nihayet bloğu, try bloğu çıktığında her zaman yürütülür. Bu, beklenmedik bir istisna meydana gelse bile nihayet bloğunun yürütülmesini sağlar. Ancak son olarak, istisna işlemeden daha fazlası için kullanışlıdır - programcının temizleme kodunun yanlışlıkla bir dönüş, devam etme veya kesme ile atlanmasını önlemesini sağlar. Hiçbir istisna beklenmese bile temizleme kodunu nihayet bloğuna koymak her zaman iyi bir uygulamadır.

Nihayet ile ilişkili aptalca mülakat sorusu şudur: Bir try {} bloğundan dönerseniz, ancak nihayetinde {} de bir dönüşünüz olursa, hangi değer döndürülür?


2
Bunun aptalca bir röportaj sorusu olduğuna katılmıyorum. Deneyimli bir Java programcısı gerektiğini anlamak için yalnızca, ne olur biliyor neden bir koyarak returnbir de finallybloğu kötü bir fikirdir. (Ve bilgi kesinlikle gerekli olmasa bile, öğrenmek için merakı olmayan bir programcı beni endişelendiriyor ...)
Stephen C

1

En kolayı:

int label = 0;
loop:while(true) {
    switch(state) {
        case 0:
            // Some code
            state = 5;
            break;

        case 2:
            // Some code
            state = 4;
            break;
        ...
        default:
            break loop;
    }
}

0

Aşağıdaki kodu deneyin. Benim için çalışıyor.

for (int iTaksa = 1; iTaksa <=8; iTaksa++) { // 'Count 8 Loop is  8 Taksa

    strTaksaStringStar[iCountTaksa] = strTaksaStringCount[iTaksa];

    LabelEndTaksa_Exit : {
        if (iCountTaksa == 1) { //If count is 6 then next it's 2
            iCountTaksa = 2;
            break  LabelEndTaksa_Exit;
        }

        if (iCountTaksa == 2) { //If count is 2 then next it's 3
            iCountTaksa = 3;
            break  LabelEndTaksa_Exit;
        }

        if (iCountTaksa == 3) { //If count is 3 then next it's 4
            iCountTaksa = 4;
            break  LabelEndTaksa_Exit;
        }

        if (iCountTaksa == 4) { //If count is 4 then next it's 7
            iCountTaksa = 7;
            break  LabelEndTaksa_Exit;
        }

        if (iCountTaksa == 7) { //If count is 7 then next it's 5
            iCountTaksa = 5;
            break  LabelEndTaksa_Exit;
        }

        if (iCountTaksa == 5) { //If count is 5 then next it's 8
            iCountTaksa = 8;
            break  LabelEndTaksa_Exit;
        }

        if (iCountTaksa == 8) { //If count is 8 then next it's 6
            iCountTaksa = 6;
            break  LabelEndTaksa_Exit;
        }

        if (iCountTaksa == 6) { //If count is 6 then loop 1  as 1 2 3 4 7 5 8 6  --> 1
            iCountTaksa = 1;
            break  LabelEndTaksa_Exit;
        }
    }   //LabelEndTaksa_Exit : {

} // "for (int iTaksa = 1; iTaksa <=8; iTaksa++) {"

-1

Gitmeye alternatif olarak etiketli bir ara kullanın.


Bu cevap her zaman gereksizdi ve her zaman gereksizdi
Stephen C

-1

Java'da yoktur goto, çünkü kodu yapılandırılmamış ve okunması belirsiz hale getirir . Ancak kullanabilirsiniz breakve continueolarak uygar şeklinde Goto onun sorunsuz.


Ara kullanarak ileri atlamak -

ahead: {
    System.out.println("Before break");
    break ahead;
    System.out.println("After Break"); // This won't execute
}
// After a line break ahead, the code flow starts from here, after the ahead block
System.out.println("After ahead");

Çıktı :

Before Break
After ahead

Continue kullanarak geri atlama

before: {
    System.out.println("Continue");
    continue before;
}

Bu , satır her yürütüldüğünde, kod akışı yeniden başlayacağı için sonsuz bir döngü ile sonuçlanacaktır .continue beforebefore


2
Continue kullanarak geriye doğru atlama örneğiniz yanlış. Bir döngünün gövdesi dışında "devam et" seçeneğini kullanamazsınız, bu bir derleme hatasına neden olur. Bir döngü içinde, ancak, devam sadece döngü koşullu atlar. Yani while (doğru) {devam;} gibi bir şey sonsuz bir döngü olurdu, ama bu konu için while (doğru) {} da öyle.
Ben Holland
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.