CodeGym /Java Blog /Acak /Kelas dalam dalam metode lokal
John Squirrels
Level 41
San Francisco

Kelas dalam dalam metode lokal

Dipublikasikan di grup Acak
Hai! Mari kita bicara tentang jenis kelas bersarang lainnya. Saya berbicara tentang kelas lokal (kelas dalam metode-lokal). Sebelum menyelam, pertama-tama kita harus mengingat tempatnya dalam struktur kelas bersarang. Kelas dalam dalam metode lokal - 2Dari diagram kami, kami dapat melihat bahwa kelas lokal adalah subspesies dari kelas dalam, yang telah kami bicarakan secara rinci di materi sebelumnya . Namun, kelas lokal memiliki sejumlah ciri dan perbedaan penting dari kelas dalam biasa. Hal utama ada dalam deklarasi mereka: Kelas lokal hanya dideklarasikan dalam blok kode. Paling sering, deklarasi ini ada di dalam beberapa metode kelas luar. Misalnya, mungkin terlihat 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 menginstal Java 7, kode ini tidak akan dikompilasi saat disisipkan ke IDEA. Kami akan membicarakan alasannya di akhir pelajaran. Singkatnya, cara kerja kelas lokal sangat bergantung pada versi bahasanya. Jika kode ini tidak dapat dikompilasi untuk Anda, Anda dapat mengganti versi bahasa di IDEA ke Java 8, atau menambahkan kata finalke parameter metode sehingga terlihat seperti ini: validatePhoneNumber(final String number). Setelah itu, semuanya akan bekerja. Ini adalah program kecil yang memvalidasi nomor telepon. Metodenya validatePhoneNumber()mengambil string sebagai input dan menentukan apakah itu nomor telepon. Dan di dalam metode ini, kami mendeklarasikan PhoneNumberkelas lokal kami. Anda mungkin bertanya mengapa. Mengapa tepatnya kita mendeklarasikan kelas di dalam metode? Mengapa tidak menggunakan inner class biasa? Benar, kita bisa membuatPhoneNumberkelas kelas batin. Tetapi solusi terakhir bergantung pada struktur dan tujuan program Anda. Mari kita ingat kembali contoh kita dari pelajaran tentang kelas dalam:

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 dalam sepeda. Apa bedanya? Pertama-tama, cara kelas digunakan berbeda. Kelas HandleBarpada contoh kedua adalah entitas yang lebih kompleks daripada PhoneNumberkelas pada contoh pertama. Pertama, HandleBarmemiliki publik rightdan leftmetode (ini bukan setter/getter). Kedua, tidak mungkin untuk memprediksi sebelumnya di mana kita membutuhkannya dan Bicyclekelas luarnya. Mungkin ada lusinan tempat dan metode yang berbeda, bahkan dalam satu program. Tetapi dengan PhoneNumberkelas, semuanya jauh lebih sederhana. Program kami sangat sederhana. Ini hanya memiliki satu tujuan: untuk memeriksa apakah suatu nomor adalah nomor telepon yang valid. Dalam kebanyakan kasus, kamiPhoneNumberValidatorbahkan tidak akan menjadi program mandiri, melainkan bagian dari logika otorisasi untuk program yang lebih besar. Misalnya, berbagai situs sering meminta nomor telepon saat pengguna mendaftar. Jika Anda memasukkan sesuatu yang tidak masuk akal alih-alih angka, situs web akan melaporkan kesalahan: "Ini bukan nomor telepon!" Pengembang situs web semacam itu (atau lebih tepatnya, mekanisme otorisasi penggunanya) dapat memasukkan sesuatu yang mirip dengan kamiPhoneNumberValidatordalam kode mereka. Dengan kata lain, kita memiliki satu kelas luar dengan satu metode, yang akan digunakan di satu tempat dalam program dan tidak di tempat lain. Dan jika digunakan, maka tidak ada yang berubah di dalamnya: satu metode melakukan tugasnya - dan hanya itu. Dalam hal ini, karena semua logika dikumpulkan ke dalam satu metode, akan jauh lebih mudah dan tepat untuk mengenkapsulasi kelas tambahan di sana. Ia tidak memiliki metode sendiri kecuali pengambil dan penyetel. Faktanya, kami hanya membutuhkan data dari konstruktor. Itu tidak terlibat dalam metode lain. Oleh karena itu, tidak ada alasan untuk mengambil informasi tentangnya di luar satu-satunya metode yang digunakan. Kami juga memberikan contoh di mana kelas lokal dideklarasikan dalam sebuah metode, tetapi ini bukan satu-satunya pilihan. Itu dapat dideklarasikan hanya dalam blok kode:

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 forlingkaran!

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 kasus seperti itu sangat jarang. Dalam kebanyakan kasus, deklarasi akan terjadi di dalam metode. Jadi, kami menemukan deklarasi, dan kami juga berbicara tentang "filosofi" :) Fitur dan perbedaan tambahan apa yang dimiliki kelas lokal dibandingkan dengan kelas dalam? Objek kelas lokal tidak dapat dibuat di luar metode atau blok yang dideklarasikan. Bayangkan kita memerlukan generatePhoneNumber()metode yang akan menghasilkan nomor telepon acak dan mengembalikan PhoneNumberobjek. Dalam situasi kami saat ini, kami tidak dapat membuat metode seperti itu di kelas validator 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() {

   }

}
Fitur penting lainnya dari kelas lokal adalah kemampuan untuk mengakses variabel lokal dan parameter metode. Jika Anda lupa, variabel yang dideklarasikan di dalam metode dikenal sebagai variabel "lokal". Artinya, jika kita membuat String usCountryCodevariabel lokal di dalam validatePhoneNumber()metode karena suatu alasan, kita dapat mengaksesnya dari PhoneNumberkelas lokal. Namun, ada banyak kehalusan yang bergantung pada versi bahasa yang digunakan dalam program. Di awal pelajaran, kami mencatat bahwa kode untuk salah satu contoh mungkin tidak dapat dikompilasi di Java 7, ingat? Sekarang mari pertimbangkan alasannya :) Di Java 7, kelas lokal dapat mengakses variabel lokal atau parameter metode hanya jika dideklarasikan seperti finaldalam metode:

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 kompiler menghasilkan dua kesalahan. Dan semuanya beres 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 kode dari awal pelajaran tidak dapat dikompilasi: di Java 7, kelas lokal hanya memiliki akses ke finalparameter metode dan finalvariabel lokal. Di Java 8, perilaku kelas lokal telah berubah. Dalam versi bahasa ini, kelas lokal memiliki akses tidak hanya ke finalvariabel dan parameter lokal, tetapi juga ke yang effective-final. Effective-finaladalah variabel yang nilainya tidak berubah sejak inisialisasi. Misalnya, di Java 8, kita dapat dengan mudah menampilkan usCountryCodevariabel di konsol, meskipun 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 mengubah nilai variabel segera setelah inisialisasi, kode tidak akan dikompilasi.

public void validatePhoneNumber(String number) {

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

    class PhoneNumber {

       public void printUsCountryCode() {

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

    }

   // ...number validation code
}
Tidak heran kelas lokal adalah subspesies dari konsep kelas dalam! Mereka juga memiliki karakteristik yang sama. Kelas lokal memiliki akses ke semua bidang (bahkan pribadi) dan metode kelas luar: baik statis maupun non-statis. Misalnya, mari tambahkan String phoneNumberRegexbidang statis ke kelas validator kita:

public class PhoneNumberValidator {

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

   public void validatePhoneNumber(String phoneNumber) {
       class PhoneNumber {
          
           // ......
       }
   }
}
Validasi akan dilakukan dengan menggunakan variabel statis ini. Metode memeriksa apakah string yang diteruskan berisi karakter yang tidak cocok dengan ekspresi reguler " [^0-9]" (yaitu, karakter apa pun yang bukan angka dari 0 hingga 9). Kita dapat dengan mudah mengakses variabel ini dari kelas lokal PhoneNumber. Misalnya, tulis pengambil:

public String getPhoneNumberRegex() {
  
   return phoneNumberRegex;
}
Kelas lokal mirip dengan kelas dalam, karena mereka tidak dapat mendefinisikan atau mendeklarasikan anggota statis apa pun. Kelas lokal dalam metode statis hanya dapat mereferensikan anggota statis dari kelas terlampir. Misalnya, jika Anda tidak mendefinisikan variabel (bidang) dari kelas terlampir sebagai statis, maka kompiler Java akan menghasilkan kesalahan: "Variabel non-statis tidak dapat dirujuk dari konteks statis." Kelas lokal tidak statis, karena mereka memiliki akses ke anggota instance di blok terlampir. Akibatnya, mereka tidak dapat memuat sebagian besar jenis deklarasi statis. Anda tidak dapat mendeklarasikan antarmuka di dalam blok: antarmuka pada dasarnya statis. Kode ini tidak dapat dikompilasi:

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 sebuah antarmuka dideklarasikan di dalam kelas luar, PhoneNumberkelas tersebut dapat mengimplementasikannya:

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
   }
}
Penginisialisasi statis (blok inisialisasi) atau antarmuka tidak dapat dideklarasikan di kelas lokal. Tetapi kelas lokal dapat memiliki anggota statis, asalkan mereka adalah variabel konstanta ( static final). Dan sekarang Anda tahu tentang kelas lokal, kawan! Seperti yang Anda lihat, mereka memiliki banyak perbedaan dari kelas batin biasa. Kami bahkan harus mempelajari fitur versi bahasa tertentu untuk memahami cara kerjanya :) Di pelajaran berikutnya, kita akan berbicara tentang kelas dalam anonim — grup terakhir dari kelas bersarang. Semoga berhasil dalam studi Anda! :)
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION