JavaRush /Blog Java /Random-MS /Kelas dalaman dalam kaedah tempatan

Kelas dalaman dalam kaedah tempatan

Diterbitkan dalam kumpulan
hello! Mari kita bercakap tentang satu lagi jenis kelas bersarang. Iaitu, tentang kelas tempatan (Kaedah kelas dalaman tempatan). Perkara pertama yang perlu anda ingat sebelum belajar ialah tempat mereka dalam struktur kelas bersarang. Kelas dalaman dalam kaedah tempatan - 2Berdasarkan rajah kami, kami boleh memahami bahawa kelas tempatan ialah subjenis kelas dalaman, yang kami bincangkan secara terperinci dalam salah satu bahan sebelumnya . Walau bagaimanapun, kelas tempatan mempunyai beberapa ciri penting dan perbezaan daripada kelas dalaman. Kuncinya adalah dalam pengisytiharan mereka: Kelas tempatan diisytiharkan hanya dalam blok kod. Selalunya - di dalam beberapa kaedah kelas luaran. 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;
           }
       }

       //...code валидации номера
   }
}
PENTING!Kod ini tidak akan dikompil apabila ditampal ke dalam IDEA jika anda telah memasang Java 7. Kami akan membincangkan sebab-sebabnya pada penghujung kuliah. Dalam beberapa perkataan, kerja kelas tempatan 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 ini semuanya akan berfungsi. Ini adalah program kecil - pengesah nombor telefon. Kaedahnya validatePhoneNumber()mengambil rentetan sebagai input dan menentukan sama ada ia adalah nombor telefon. Dan di dalam kaedah ini kami mengisytiharkan kelas tempatan kami PhoneNumber. Anda mungkin mempunyai soalan logik: mengapa? Mengapa mengisytiharkan kelas di dalam kaedah? Mengapa tidak menggunakan kelas dalaman biasa? Sesungguhnya, seseorang boleh melakukan ini: menjadikan kelas PhoneNumberdalaman. Perkara lain ialah keputusan akhir bergantung pada struktur dan tujuan program anda. Mari kita ingat contoh kita dari kuliah tentang kelas dalaman:
public class Bicycle {

   private String model;
   private int mawWeight;

   public Bicycle(String model, int mawWeight) {
       this.model = model;
       this.mawWeight = mawWeight;
   }

   public void start() {
       System.out.println("Go!");
   }

   public class HandleBar {

       public void right() {
           System.out.println("Steering wheel to the right!");
       }

       public void left() {

           System.out.println("Steering wheel to the left!");
       }
   }
}
Di dalamnya kami menjadikan HandleBar(hendal) kelas dalaman basikal. Apa perbezaannya? Pertama sekali, dalam menggunakan kelas. Kelas HandleBardari contoh kedua adalah entiti yang lebih kompleks daripada PhoneNumberdari yang pertama. Pertama, y HandleBar​​mempunyai kaedah awam rightdan left(bukan setter dan getter). Kedua, kita tidak boleh meramalkan terlebih dahulu di mana Bicyclekita mungkin memerlukannya dan kelas luarannya - ini boleh menjadi berpuluh-puluh tempat dan kaedah yang berbeza, walaupun dalam program yang sama. Tetapi dengan kelas PhoneNumbersemuanya lebih mudah. Program kami sangat mudah. Ia hanya mempunyai satu fungsi - untuk menyemak sama ada nombor itu adalah nombor telefon. Dalam kebanyakan kes, program kami PhoneNumberValidatortidak akan menjadi program bebas, tetapi hanya sebahagian dalam logik kebenaran untuk program utama. Sebagai contoh, di pelbagai laman web, semasa mendaftar, anda sering diminta memasukkan nombor telefon. Dan jika anda menaip beberapa karut dan bukannya nombor, tapak akan memaparkan ralat: "Ini bukan nombor telefon!" Untuk pengendalian tapak sedemikian (atau lebih tepat, mekanisme kebenaran pengguna), pembangunnya boleh memasukkan analog kami dalam kod PhoneNumberValidator. Dalam erti kata lain, kami mempunyai satu kelas luaran dengan satu kaedah yang akan digunakan di satu tempat dalam program dan tidak di tempat lain. Dan jika ia berlaku, maka tiada apa yang akan berubah di dalamnya: satu kaedah melakukan tugasnya - itu sahaja. Dalam kes ini, kerana semua logik kerja dikumpulkan dalam satu kaedah, ia akan menjadi lebih mudah dan betul untuk merangkum kelas tambahan di sana. Ia tidak mempunyai kaedah tersendiri selain pengambil dan penetap. Kami pada asasnya hanya memerlukan data pembina daripadanya. Ia tidak digunakan dalam kaedah lain. Oleh itu, tiada sebab untuk memanjangkan maklumat mengenainya melebihi kaedah tunggal di mana ia digunakan. Kami memberikan contoh mengisytiharkan kelas tempatan dalam kaedah, tetapi ini bukan satu-satunya kemungkinan. 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) {


       //...code валидации номера
   }
}
Ataupun dalam satu gelung for!
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;
               }
           }

           //...Howая-то логика
       }

       //...code валидации номера
   }
}
Tetapi kes sedemikian sangat jarang berlaku. Dalam kebanyakan kes, pengisytiharan masih akan berlaku di dalam kaedah. Jadi, kami berurusan dengan pengumuman itu, kami juga bercakap tentang "falsafah" :) Apakah ciri dan perbezaan lain yang ada pada kelas tempatan daripada kelas dalaman? Objek kelas tempatan tidak boleh dibuat di luar kaedah atau blok di mana ia diisytiharkan. Bayangkan kita memerlukan kaedah generatePhoneNumber()yang menjana nombor telefon rawak dan mengembalikan PhoneNumber. Kami tidak akan dapat mencipta kaedah sedemikian dalam kelas pengesah kami dalam situasi semasa:
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;
           }
       }

       //...code валидации номера
   }

   //ошибка! компилятор не понимает, что это за класс - PhoneNumber
   public PhoneNumber generatePhoneNumber() {

   }

}
Satu lagi ciri penting kelas tempatan ialah keupayaan untuk mengakses pembolehubah tempatan dan parameter kaedah. Sekiranya anda terlupa, "tempatan" ialah pembolehubah yang diisytiharkan dalam kaedah. String russianCountryCodeIaitu, jika kita mencipta pembolehubah tempatan di dalam kaedah untuk beberapa tujuan kita validatePhoneNumber(), kita boleh mengaksesnya daripada kelas tempatan PhoneNumber. Walau bagaimanapun, terdapat banyak kehalusan di sini yang bergantung pada versi bahasa yang digunakan dalam program ini. Pada permulaan kuliah, kami membuat nota bahawa kod dalam salah satu contoh mungkin tidak disusun dalam Java 7, ingat? Sekarang mari kita lihat sebab untuk ini :) Dalam Java 7, kelas tempatan hanya boleh mengakses pembolehubah tempatan atau parameter kaedah jika ia diisytiharkan dalam kaedah sebagai final:
public void validatePhoneNumber(String number) {

   String russianCountryCode = "+7";

   class PhoneNumber {

       private String phoneNumber;

       //ошибка! параметр метода должен быть объявлен How final!
       public PhoneNumber() {
           this.phoneNumber = number;
       }

       public void printRussianCountryCode() {

           //ошибка! локальная переменная должна быть объявлена How final!
           System.out.println(russianCountryCode);
       }

   }

   //...code валидации номера
}
Di sini pengkompil melemparkan dua ralat. Tetapi di sini semuanya teratur:
public void validatePhoneNumber(final String number) {

   final String russianCountryCode = "+7";

    class PhoneNumber {

       private String phoneNumber;


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

       public void printRussianCountryCode() {

           System.out.println(russianCountryCode);
       }

    }

   //...code валидации номера
}
Sekarang anda tahu sebab mengapa kod pada permulaan kuliah tidak disusun: kelas tempatan di Java 7 hanya mempunyai akses kepada finalparameter -method dan finalpembolehubah -local. Di Java 8, tingkah laku kelas tempatan telah berubah. Dalam versi bahasa ini, kelas tempatan mempunyai akses bukan sahaja kepada finalpembolehubah dan parameter -local, tetapi juga kepada effective-final. Effective-finalialah pembolehubah yang nilainya tidak berubah sejak permulaan. Sebagai contoh, dalam Java 8 kita boleh memaparkan pembolehubah dengan mudah ke konsol russianCountryCode, walaupun ia bukan final. Perkara utama ialah ia tidak mengubah maknanya. Dalam contoh ini, semuanya berfungsi sebagaimana mestinya:
public void validatePhoneNumber(String number) {

  String russianCountryCode = "+7";

    class PhoneNumber {

       public void printRussianCountryCode() {

           //в Java 7 здесь была бы ошибка
           System.out.println(russianCountryCode);
       }

    }

   //...code валидации номера
}
Tetapi jika kita menukar nilai pembolehubah sejurus selepas permulaan, kod itu tidak akan disusun.
public void validatePhoneNumber(String number) {

  String russianCountryCode = "+7";
  russianCountryCode = "+8";

    class PhoneNumber {

       public void printRussianCountryCode() {

           //error!
           System.out.println(russianCountryCode);
       }

    }

   //...code валидации номера
}
Tetapi bukan untuk apa-apa kelas tempatan ialah subjenis kelas dalaman! Mereka juga mempunyai titik persamaan. Kelas tempatan mempunyai akses kepada semua (walaupun peribadi) medan dan kaedah kelas luar: statik dan bukan statik. Sebagai contoh, mari tambah medan statik pada kelas pengesah kami String phoneNumberRegex:
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 dihantar kepadanya mengandungi aksara yang tidak sepadan dengan ungkapan biasa " [^0-9]" (iaitu, aksara itu bukan nombor 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 dalam 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, pengkompil Java menjana ralat: "Pembolehubah bukan statik tidak boleh dirujuk daripada konteks statik." Kelas tempatan tidak statik kerana mereka mempunyai akses kepada ahli contoh blok yang mengandungi. Oleh itu, ia tidak boleh mengandungi kebanyakan jenis pengisytiharan statik. Anda tidak boleh mengisytiharkan antara muka di dalam blok; Antara muka bersifat statik. Kod ini tidak akan disusun:
public class PhoneNumberValidator {
   public static void validatePhoneNumber(String number) {
       interface I {}

       class PhoneNumber implements I{
           private String phoneNumber;

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

       //...code валидации номера
   }
}
Tetapi jika antara muka diisytiharkan di dalam kelas luar, kelas PhoneNumberboleh melaksanakannya:
public class PhoneNumberValidator {
   interface I {}

   public static void validatePhoneNumber(String number) {

       class PhoneNumber implements I{
           private String phoneNumber;

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

       //...code валидации номера
   }
}
Kelas tempatan tidak boleh mengisytiharkan pemula statik (blok permulaan) atau antara muka. Tetapi kelas tempatan boleh mempunyai ahli statik, dengan syarat ia pembolehubah malar ( static final). Itulah mereka, kelas tempatan! Seperti yang anda lihat, mereka mempunyai banyak perbezaan daripada kelas dalaman. Kami juga terpaksa menyelami ciri versi bahasa untuk memahami cara ia berfungsi :) Dalam kuliah seterusnya, kita akan bercakap tentang kelas dalaman tanpa nama - kumpulan terakhir kelas bersarang. Semoga berjaya dengan pelajaran anda! :)
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION