CodeGym /Blog Java /rawak /Kelas dalaman dalam kaedah tempatan
John Squirrels
Tahap
San Francisco

Kelas dalaman dalam kaedah tempatan

Diterbitkan dalam kumpulan
Hai! Mari kita bercakap tentang satu lagi jenis kelas bersarang. Saya bercakap tentang kelas tempatan (kaedah-kelas dalaman tempatan). Sebelum menyelam, kita mesti ingat dahulu tempat mereka dalam struktur kelas bersarang. Kelas dalaman dalam kaedah tempatan - 2Daripada rajah kami, kami dapat melihat bahawa kelas tempatan adalah subspesies kelas dalaman, yang kami bincangkan secara terperinci dalam bahan sebelumnya . Walau bagaimanapun, kelas tempatan mempunyai beberapa ciri penting dan perbezaan daripada kelas dalaman biasa. Perkara utama adalah dalam pengisytiharan mereka: Kelas tempatan diisytiharkan hanya dalam blok kod. Selalunya, pengisytiharan ini berada di dalam beberapa kaedah kelas luar. Sebagai contoh, ia mungkin kelihatan seperti ini:

public class PhoneNumberValidator {

   public void validatePhoneNumber(String number) {

        class PhoneNumber {

           private String phoneNumber;

           public PhoneNumber() {
               this.phoneNumber = number;
           }

           public String getPhoneNumber() {
               return phoneNumber;
           }

           public void setPhoneNumber(String phoneNumber) {
               this.phoneNumber = phoneNumber;
           }
       }

       // ...number validation code
   }
}
PENTING!Jika anda telah memasang Java 7, kod ini tidak akan disusun apabila ditampal ke dalam IDEA. Kita akan bercakap tentang sebab-sebab ini pada akhir pelajaran. Ringkasnya, cara kelas tempatan berfungsi sangat bergantung pada versi bahasa. Jika kod ini tidak disusun untuk anda, anda boleh menukar versi bahasa dalam IDEA kepada Java 8 atau menambah perkataan finalpada parameter kaedah supaya ia kelihatan seperti ini: validatePhoneNumber(final String number). Selepas itu, semuanya akan berfungsi. Ini adalah program kecil yang mengesahkan nombor telefon. Kaedahnya validatePhoneNumber()mengambil rentetan sebagai input dan menentukan sama ada ia adalah nombor telefon. Dan di dalam kaedah ini, kami mengisytiharkan PhoneNumberkelas tempatan kami. Anda mungkin bertanya mengapa. Mengapa sebenarnya kita mengisytiharkan kelas di dalam kaedah? Mengapa tidak menggunakan kelas dalaman biasa? Benar, kami boleh membuatPhoneNumberkelas kelas dalaman. Tetapi penyelesaian akhir bergantung pada struktur dan tujuan program anda. Mari kita ingat contoh kita dari pelajaran tentang kelas dalaman:

public class Bicycle {

   private String model;
   private int maxWeight;

   public Bicycle(String model, int maxWeight) {
       this.model = model;
       this.maxWeight = maxWeight;
   }
  
   public void start() {
       System.out.println("Let's go!");
   }

   public class HandleBar {

       public void right() {
           System.out.println("Steer right!");
       }

       public void left() {

           System.out.println("Steer left!");
       }
   }
}
Di dalamnya, kami membuat HandleBarkelas dalaman basikal. Apa perbezaannya? Pertama sekali, cara kelas digunakan adalah berbeza. Kelas HandleBardalam contoh kedua adalah entiti yang lebih kompleks daripada PhoneNumberkelas dalam contoh pertama. Pertama, HandleBarmempunyai awam rightdan leftkaedah (ini bukan setter/getters). Kedua, adalah mustahil untuk meramalkan terlebih dahulu di mana kita mungkin memerlukannya dan Bicyclekelas luarnya. Mungkin terdapat berpuluh-puluh tempat dan kaedah yang berbeza, walaupun dalam satu program. Tetapi dengan PhoneNumberkelas, semuanya lebih mudah. Program kami sangat mudah. Ia hanya mempunyai satu tujuan: untuk menyemak sama ada nombor adalah nombor telefon yang sah. Dalam kebanyakan kes, kamiPhoneNumberValidatormalah tidak akan menjadi program kendiri, sebaliknya sebahagian daripada logik kebenaran untuk program yang lebih besar. Sebagai contoh, pelbagai laman web sering meminta nombor telefon apabila pengguna mendaftar. Jika anda memasukkan beberapa karut dan bukannya nombor, tapak web akan melaporkan ralat: "Ini bukan nombor telefon!" Pembangun tapak web sedemikian (atau lebih tepat, mekanisme kebenaran penggunanya) boleh memasukkan sesuatu yang serupa dengan kamiPhoneNumberValidatordalam kod mereka. Dalam erti kata lain, kami mempunyai satu kelas luar dengan satu kaedah, yang akan digunakan di satu tempat dalam program dan tidak di tempat lain. Dan jika ia digunakan, maka tiada apa yang akan berubah di dalamnya: satu kaedah melakukan tugasnya — dan itu sahaja. Dalam kes ini, kerana semua logik dikumpulkan ke dalam satu kaedah, ia akan menjadi lebih mudah dan sesuai untuk merangkum kelas tambahan di sana. Ia tidak mempunyai kaedah sendiri kecuali pengambil dan penetap. Sebenarnya, kami hanya memerlukan data daripada pembina. Ia tidak terlibat dalam kaedah lain. Sehubungan itu, tidak ada sebab untuk mengambil maklumat mengenainya di luar satu-satunya kaedah di mana ia digunakan. Kami juga memberikan contoh di mana kelas tempatan diisytiharkan dalam kaedah, tetapi ini bukan satu-satunya pilihan. Ia boleh diisytiharkan hanya dalam blok kod:

public class PhoneNumberValidator {
  
   {
       class PhoneNumber {

           private String phoneNumber;

           public PhoneNumber(String phoneNumber) {
               this.phoneNumber = phoneNumber;
           }
       }

   }

   public void validatePhoneNumber(String phoneNumber) {

      
       // ...number validation code
   }
}
Atau bahkan dalam forgelung!

public class PhoneNumberValidator {
  

   public void validatePhoneNumber(String phoneNumber) {

       for (int i = 0; i < 10; i++) {

           class PhoneNumber {

               private String phoneNumber;

               public PhoneNumber(String phoneNumber) {
                   this.phoneNumber = phoneNumber;
               }
           }
          
           // ...some logic
       }

       // ...number validation code
   }
}
Tetapi kes sedemikian sangat jarang berlaku. Dalam kebanyakan kes, pengisytiharan akan berlaku di dalam kaedah. Jadi, kami memikirkan pengisytiharan, dan kami juga bercakap tentang "falsafah" :) Apakah ciri dan perbezaan tambahan yang ada pada kelas tempatan berbanding dengan kelas dalaman? Objek kelas tempatan tidak boleh dibuat di luar kaedah atau blok di mana ia diisytiharkan. Bayangkan bahawa kita memerlukan generatePhoneNumber()kaedah yang akan menghasilkan nombor telefon rawak dan mengembalikan PhoneNumberobjek. Dalam situasi semasa kami, kami tidak boleh mencipta kaedah sedemikian dalam kelas pengesah kami:

public class PhoneNumberValidator {

   public void validatePhoneNumber(String number) {

        class PhoneNumber {

           private String phoneNumber;

           public PhoneNumber() {
               this.phoneNumber = number;
           }

           public String getPhoneNumber() {
               return phoneNumber;
           }

           public void setPhoneNumber(String phoneNumber) {
               this.phoneNumber = phoneNumber;
           }
       }

       // ...number validation code
   }

   // Error! The compiler does not recognize the PhoneNumber class
   public PhoneNumber generatePhoneNumber() {

   }

}
Satu lagi ciri penting kelas tempatan ialah keupayaan untuk mengakses pembolehubah tempatan dan parameter kaedah. Sekiranya anda terlupa, pembolehubah yang diisytiharkan di dalam kaedah dikenali sebagai pembolehubah "tempatan". Iaitu, jika kita mencipta String usCountryCodepembolehubah tempatan dalam validatePhoneNumber()kaedah tertentu, kita boleh mengaksesnya daripada PhoneNumberkelas tempatan. Walau bagaimanapun, terdapat banyak kehalusan yang bergantung pada versi bahasa yang digunakan dalam program ini. Pada permulaan pelajaran, kami perhatikan bahawa kod untuk salah satu contoh mungkin tidak disusun dalam Java 7, ingat? Sekarang mari kita pertimbangkan sebab untuk ini :) Dalam Java 7, kelas tempatan boleh mengakses pembolehubah tempatan atau parameter kaedah hanya jika ia diisytiharkan seperti finaldalam kaedah:

public void validatePhoneNumber(String number) {

   String usCountryCode = "+1";

   class PhoneNumber {

       private String phoneNumber;

       // Error! The method parameter must be declared as final!
       public PhoneNumber() {
           this.phoneNumber = number;
       }

       public void printUsCountryCode() {

           // Error! The local variable must be declared as final!
           System.out.println(usCountryCode);
       }

   }

   // ...number validation code
}
Di sini pengkompil menghasilkan dua ralat. Dan semuanya teratur di sini:

public void validatePhoneNumber(final String number) {

   final String usCountryCode = "+1";

    class PhoneNumber {

       private String phoneNumber;

       
       public PhoneNumber() {
           this.phoneNumber = number;
       }

       public void printUsCountryCode() {

           System.out.println(usCountryCode);
       }

    }

   // ...number validation code
}
Sekarang anda tahu mengapa kod dari permulaan pelajaran tidak akan disusun: dalam Java 7, kelas tempatan hanya mempunyai akses kepada finalparameter kaedah dan finalpembolehubah setempat. Di Java 8, tingkah laku kelas tempatan telah berubah. Dalam versi bahasa ini, kelas tempatan mempunyai akses bukan sahaja kepada finalpembolehubah dan parameter tempatan, tetapi juga kepada mereka yang effective-final. Effective-finalialah pembolehubah yang nilainya tidak berubah sejak permulaan. Sebagai contoh, dalam Java 8, kita boleh memaparkan usCountryCodepembolehubah dengan mudah pada konsol, walaupun ia bukan final. Yang penting nilainya tidak berubah. Dalam contoh berikut, semuanya berfungsi sebagaimana mestinya:

public void validatePhoneNumber(String number) {

  String usCountryCode = "+1";

    class PhoneNumber {

       public void printUsCountryCode() {

           // Java 7 would produce an error here
           System.out.println(usCountryCode);
       }

    }

   // ...number validation code
}
Tetapi jika kita menukar nilai pembolehubah sejurus selepas permulaan, kod itu tidak akan disusun.

public void validatePhoneNumber(String number) {

  String usCountryCode = "+1";
  usCountryCode = "+8";

    class PhoneNumber {

       public void printUsCountryCode() {

           // Error!
           System.out.println(usCountryCode);
       }

    }

   // ...number validation code
}
Tidak hairanlah kelas tempatan adalah subspesies konsep kelas dalam! Mereka juga mempunyai ciri umum. Kelas tempatan mempunyai akses kepada semua (walaupun peribadi) medan dan kaedah kelas luar: statik dan bukan statik. Sebagai contoh, mari tambah String phoneNumberRegexmedan statik pada kelas pengesah kami:

public class PhoneNumberValidator {

   private static String phoneNumberRegex = "[^0-9]";

   public void validatePhoneNumber(String phoneNumber) {
       class PhoneNumber {
          
           // ......
       }
   }
}
Pengesahan akan dilakukan menggunakan pembolehubah statik ini. Kaedah menyemak sama ada rentetan yang diluluskan mengandungi aksara yang tidak sepadan dengan ungkapan biasa " [^0-9]" (iaitu, mana-mana aksara yang bukan digit dari 0 hingga 9). Kita boleh mengakses pembolehubah ini dengan mudah daripada kelas tempatan PhoneNumber. Sebagai contoh, tulis pengambil:

public String getPhoneNumberRegex() {
  
   return phoneNumberRegex;
}
Kelas tempatan adalah serupa dengan kelas dalaman, kerana mereka tidak boleh menentukan atau mengisytiharkan mana-mana ahli statik. Kelas tempatan dalam kaedah statik hanya boleh merujuk ahli statik kelas yang disertakan. Sebagai contoh, jika anda tidak mentakrifkan pembolehubah (medan) kelas yang disertakan sebagai statik, maka pengkompil Java menjana ralat: "Pembolehubah bukan statik tidak boleh dirujuk daripada konteks statik." Kelas tempatan tidak statik, kerana mereka mempunyai akses kepada ahli contoh dalam blok yang disertakan. Akibatnya, ia tidak boleh mengandungi kebanyakan jenis pengisytiharan statik. Anda tidak boleh mengisytiharkan antara muka di dalam blok: antara muka sememangnya statik. Kod ini tidak menyusun:

public class PhoneNumberValidator {
   public static void validatePhoneNumber(String number) {
       interface I {}
      
       class PhoneNumber implements I{
           private String phoneNumber;

           public PhoneNumber() {
               this.phoneNumber = number;
           }
       }

       // ...number validation code
   }
}
Tetapi jika antara muka diisytiharkan di dalam kelas luar, PhoneNumberkelas boleh melaksanakannya:

public class PhoneNumberValidator {
   interface I {}
  
   public static void validatePhoneNumber(String number) {
      
       class PhoneNumber implements I{
           private String phoneNumber;

           public PhoneNumber() {
               this.phoneNumber = number;
           }
       }

       // ...number validation code
   }
}
Pemula statik (blok permulaan) atau antara muka tidak boleh diisytiharkan dalam kelas tempatan. Tetapi kelas tempatan boleh mempunyai ahli statik, dengan syarat ia pembolehubah malar ( static final). Dan kini anda tahu tentang kelas tempatan, kawan-kawan! Seperti yang anda lihat, mereka mempunyai banyak perbezaan daripada kelas dalaman biasa. Kami juga terpaksa menyelidiki ciri versi bahasa tertentu untuk memahami cara ia berfungsi :) Dalam pelajaran seterusnya, kita akan bercakap tentang kelas dalaman tanpa nama — kumpulan terakhir kelas bersarang. Semoga berjaya dalam pelajaran anda! :)
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION