Apa gunanya "kelas akhir" di Jawa?


569

Saya membaca buku tentang Java dan mengatakan bahwa Anda dapat mendeklarasikan seluruh kelas sebagai final. Saya tidak bisa memikirkan apa pun di mana saya akan menggunakan ini.

Saya baru mengenal pemrograman dan saya bertanya-tanya apakah programmer benar-benar menggunakan ini pada program mereka . Jika mereka melakukannya, kapan mereka menggunakannya sehingga saya bisa memahaminya lebih baik dan tahu kapan menggunakannya.

Jika Java berorientasi objek, dan Anda mendeklarasikan kelas final, bukankah itu menghentikan gagasan kelas yang memiliki karakteristik objek?

Jawaban:


531

Pertama-tama, saya merekomendasikan artikel ini: Java: Kapan membuat kelas akhir


Jika mereka melakukannya, kapan mereka menggunakannya sehingga saya bisa memahaminya lebih baik dan tahu kapan menggunakannya.

Sebuah finalkelas hanyalah sebuah kelas yang tidak dapat diperpanjang .

(Itu tidak berarti bahwa semua referensi ke objek kelas akan bertindak seolah-olah mereka dinyatakan sebagai final.)

Ketika berguna untuk mendeklarasikan kelas sebagai final tercakup dalam jawaban atas pertanyaan ini:

Jika Java berorientasi objek, dan Anda mendeklarasikan kelas final, bukankah itu menghentikan gagasan kelas yang memiliki karakteristik objek?

Dalam arti tertentu ya.

Dengan menandai kelas sebagai final, Anda menonaktifkan fitur yang kuat dan fleksibel bahasa untuk bagian kode tersebut. Namun beberapa kelas, seharusnya tidak (dan dalam kasus-kasus tertentu tidak dapat ) dirancang untuk memperhitungkan subkelas dengan cara yang baik. Dalam kasus ini, masuk akal untuk menandai kelas sebagai final, meskipun membatasi OOP. (Namun ingat bahwa kelas final masih dapat memperpanjang kelas non-final lainnya.)


39
Untuk menambah jawaban, salah satu prinsip Java Efektif adalah untuk memilih komposisi daripada warisan. Penggunaan kata kunci terakhir juga membantu menegakkan prinsip itu.
Riggy

9
"Kamu melakukannya terutama karena alasan efisiensi dan keamanan." Saya sering mendengar pernyataan ini (bahkan Wikipedia menyatakan ini) tetapi saya masih tidak mengerti alasan di balik argumen ini. Apakah seseorang mau menjelaskan bagaimana, katakanlah, java.lang.String non-final akan berakhir dengan tidak efisien atau tidak aman?
MRA

27
@ MRA Jika saya membuat metode yang menerima String sebagai parameter, saya berasumsi bahwa itu tidak dapat diubah, karena String adalah. Sebagai hasil dari ini, saya tahu saya dapat memanggil metode apa pun pada objek String dengan aman, dan tidak mengubah String yang diteruskan. Jika saya memperpanjang String, dan mengubah implementasi substring untuk mengubah String yang sebenarnya, maka objek String yang Anda harapkan tidak dapat diubah tidak lagi dapat diubah.
Cruncher

1
@Sortofabeginner Dan segera setelah Anda mengatakan Anda ingin semua metode dan bidang String menjadi final, hanya agar Anda dapat membuat beberapa kelas dengan fungsionalitas tambahan ... Pada titik itu Anda mungkin juga cukup membuat kelas yang memiliki-string dan buat metode yang beroperasi pada string itu.
Cruncher

1
@Shay final (antara lain) digunakan untuk membuat objek tidak berubah, jadi saya tidak akan mengatakan mereka tidak ada hubungannya satu sama lain. Lihat di sini docs.oracle.com/javase/tutorial/essential/concurrency/…
Celeritas

184

Di Jawa, item dengan finalpengubah tidak dapat diubah!

Ini termasuk kelas akhir, variabel akhir, dan metode akhir:

  • Kelas terakhir tidak dapat diperpanjang oleh kelas lain mana pun
  • Variabel terakhir tidak dapat ditugaskan kembali nilai lain
  • Metode terakhir tidak dapat ditimpa

40
Pertanyaan sebenarnya adalah mengapa , bukan apa .
Francesco Menzani

8
Pernyataan, "Di Jawa, item dengan finalpengubah tidak dapat diubah!", Terlalu kategoris dan, pada kenyataannya, tidak sepenuhnya benar. Seperti yang dikatakan Grady Booch, "Sebuah objek memiliki status, perilaku, dan identitas". Sementara kita tidak bisa mengubah identitas obyek sekali referensi telah ditandai sebagai akhir, kita memiliki kesempatan untuk mengubah nya negara dengan menetapkan nilai-nilai baru untuk non-nya finalbidang (asalkan, tentu saja, memiliki mereka.) Siapa saja yang berencana untuk mendapatkan Sertifikasi Java Java (seperti 1Z0-808, dll.) harus mengingat hal ini karena mungkin ada pertanyaan tentang aspek ini pada ujian ...
Igor Soudakevitch

33

Satu skenario di mana final penting, ketika Anda ingin mencegah warisan kelas, untuk alasan keamanan. Ini memungkinkan Anda untuk memastikan bahwa kode yang Anda jalankan tidak dapat ditimpa oleh seseorang.

Skenario lain adalah untuk optimasi: Saya sepertinya ingat bahwa kompiler Java menyatukan beberapa panggilan fungsi dari kelas akhir. Jadi, jika Anda menelepon a.x()dan dideklarasikan final, kami tahu pada waktu kompilasi kode apa yang akan dan dapat sejalan dengan fungsi panggilan. Saya tidak tahu apakah ini benar-benar dilakukan, tetapi dengan final itu adalah suatu kemungkinan.


7
Inlining biasanya hanya dilakukan oleh kompiler just-in-time saat runtime. Ia bekerja tanpa akhir juga, tetapi JIT-compiler memiliki sedikit lebih banyak pekerjaan yang harus dilakukan untuk memastikan bahwa tidak ada kelas perluasan (atau bahwa kelas perluasan ini tidak menyentuh metode ini).
Paŭlo Ebermann


24

Contoh terbaik adalah

String kelas akhir publik

yang merupakan kelas abadi dan tidak dapat diperpanjang. Tentu saja, ada lebih dari sekedar membuat final kelas menjadi abadi.


Hehe, terkadang itu melindungi pengembang Rube Goldergian dari diri mereka sendiri.
Zoidberg

16

Bacaan yang relevan: Prinsip Terbuka-Terbuka oleh Bob Martin.

Kutipan kunci:

Entitas Perangkat Lunak (Kelas, Modul, Fungsi, dll.) Harus terbuka untuk Ekstensi, tetapi ditutup untuk Modifikasi.

Kata finalkunci adalah sarana untuk menegakkan ini di Jawa, apakah itu digunakan pada metode atau di kelas.


6
@Sean: Tidakkah mendeklarasikan itu finalmembuat kelas tertutup untuk ekstensi daripada terbuka? Atau apakah saya menganggapnya terlalu harfiah?
Goran Jovic

4
@Goran berlaku global untuk final, ya. Kuncinya adalah menerapkan final secara selektif di tempat-tempat di mana Anda tidak ingin modifikasi (dan tentu saja untuk memberikan kait yang baik untuk ekstensi)
Sean Patrick Floyd

26
Dalam OCP, "modifikasi" mengacu pada memodifikasi kode sumber, dan "ekstensi" mengacu pada warisan implementasi. Oleh karena itu, penggunaan finaldeklarasi kelas / metode tidak akan masuk akal jika Anda ingin kode implementasi ditutup untuk modifikasi tetapi terbuka untuk ekstensi dengan pewarisan.
Rogério

1
@Rogerio Saya telah meminjam referensi (dan interpretasi) dari Spring Framework Reference (MVC) . IMHO ini lebih masuk akal daripada versi aslinya.
Sean Patrick Floyd

Perpanjangan sudah mati. Tak berguna. Hancur. Hancur. Saya tidak peduli tentang OCP. Tidak pernah ada alasan untuk memperpanjang kelas.
Josh Woodcock

15

Jika Anda membayangkan hierarki kelas sebagai pohon (seperti di Jawa), kelas abstrak hanya bisa berupa cabang dan kelas akhir adalah yang hanya bisa berupa daun. Kelas-kelas yang termasuk dalam kategori-kategori tersebut tidak dapat berupa cabang dan daun.

Tidak ada pelanggaran prinsip OO di sini, final hanya memberikan simetri yang bagus.

Dalam praktiknya Anda ingin menggunakan final jika Anda ingin objek Anda tidak berubah atau jika Anda sedang menulis API, untuk memberi sinyal kepada pengguna API bahwa kelas tidak dimaksudkan untuk ekstensi.


13

Kata kunci finalitu sendiri berarti sesuatu sudah final dan tidak boleh dimodifikasi dengan cara apa pun. Jika suatu kelas jika ditandai finalmaka tidak dapat diperpanjang atau sub-kelas. Tetapi pertanyaannya adalah mengapa kita menandai kelas final? IMO ada berbagai alasan:

  1. Standardisasi: Beberapa kelas melakukan fungsi standar dan mereka tidak dimaksudkan untuk dimodifikasi misalnya kelas melakukan berbagai fungsi yang terkait dengan manipulasi string atau fungsi matematika dll.
  2. Alasan keamanan : Kadang-kadang kami menulis kelas yang melakukan berbagai fungsi terkait otentikasi dan kata sandi dan kami tidak ingin mereka diubah oleh orang lain.

Saya telah mendengar bahwa kelas penandaan finalmeningkatkan efisiensi tetapi terus terang saya tidak dapat menemukan argumen ini memiliki bobot yang besar.

Jika Java berorientasi objek, dan Anda mendeklarasikan final kelas, bukankah itu menghentikan gagasan kelas yang memiliki karakteristik objek?

Mungkin ya, tapi kadang-kadang itulah tujuan yang dimaksudkan. Terkadang kita melakukan itu untuk mencapai manfaat keamanan yang lebih besar, dll. Dengan mengorbankan kemampuan kelas ini untuk diperluas. Tetapi kelas final masih dapat memperpanjang satu kelas jika perlu.

Sebagai tambahan, kita harus lebih memilih komposisi daripada pewarisan dan finalkata kunci sebenarnya membantu menegakkan prinsip ini.


6

Hati-hati saat Anda membuat kelas "final". Karena jika Anda ingin menulis unit test untuk kelas akhir, Anda tidak dapat membuat subclass kelas akhir ini untuk menggunakan teknik pemecahan ketergantungan "Metode Subclass dan Override" yang dijelaskan dalam buku Michael C. Feathers "Bekerja Efektif dengan Legacy Code" . Dalam buku ini, Feathers berkata, "Serius, mudah untuk percaya bahwa disegel dan final adalah kesalahan yang salah, bahwa mereka seharusnya tidak pernah ditambahkan ke bahasa pemrograman. Tapi kesalahan sebenarnya ada pada kita. Ketika kita bergantung langsung pada perpustakaan yang di luar kendali kami, kami hanya meminta masalah. "


6

final class dapat menghindari melanggar API publik ketika Anda menambahkan metode baru

Misalkan pada versi 1 dari Anda Base kelas Anda yang Anda lakukan:

public class Base {}

dan klien melakukannya:

class Derived extends Base {
    public int method() { return 1; }
}

Kemudian jika dalam versi 2 Anda ingin menambahkan methodmetodeBase :

class Base {
    public String method() { return null; }
}

itu akan merusak kode klien.

Jika kami telah menggunakan final class Basesebagai gantinya, klien tidak akan dapat mewarisi, dan penambahan metode tidak akan merusak API.


5

Jika kelas ditandai final, itu berarti bahwa struktur kelas tidak dapat dimodifikasi oleh sesuatu yang eksternal. Di mana ini adalah yang paling terlihat adalah ketika Anda melakukan warisan polimorfik tradisional, pada dasarnya class B extends Atidak akan berhasil. Ini pada dasarnya adalah cara untuk melindungi beberapa bagian dari kode Anda (sejauh) .

Untuk memperjelas, menandai kelas finaltidak menandai bidangnya finaldan karenanya tidak melindungi properti objek tetapi struktur kelas yang sebenarnya.


1
Apa yang dimaksud dengan properti objek? Apakah ini berarti saya dapat memodifikasi variabel anggota kelas jika kelas dinyatakan final? Jadi satu-satunya tujuan kelas akhir adalah untuk mencegah warisan.
Adam Lyu

5

UNTUK MENGATASI MASALAH KELAS FINAL:

Ada dua cara untuk membuat kelas menjadi final. Yang pertama adalah menggunakan kata kunci akhir dalam deklarasi kelas:

public final class SomeClass {
  //  . . . Class contents
}

Cara kedua untuk membuat final kelas adalah mendeklarasikan semua konstruktornya sebagai pribadi:

public class SomeClass {
  public final static SOME_INSTANCE = new SomeClass(5);
  private SomeClass(final int value) {
  }

Menandai itu final akan menyelamatkan Anda dari kesulitan jika mengetahui bahwa itu adalah final, untuk menunjukkan tampilan pada kelas Tes ini. terlihat publik pada pandangan pertama.

public class Test{
  private Test(Class beanClass, Class stopClass, int flags)
    throws Exception{
    //  . . . snip . . . 
  }
}

Sayangnya, karena satu-satunya konstruktor kelas adalah privat, tidak mungkin untuk memperpanjang kelas ini. Dalam hal kelas Tes, tidak ada alasan bahwa kelas tersebut harus final. Kelas Tes adalah contoh yang baik tentang bagaimana kelas akhir implisit dapat menyebabkan masalah.

Jadi Anda harus menandainya sebagai final ketika Anda secara implisit membuat final kelas dengan menjadikannya konstruktor pribadi.


4

Kelas terakhir adalah kelas yang tidak bisa diperpanjang. Juga metode dapat dinyatakan sebagai final untuk menunjukkan bahwa tidak dapat diganti oleh subclass.

Mencegah kelas menjadi subkelas bisa sangat berguna jika Anda menulis API atau pustaka dan ingin menghindari diperpanjang untuk mengubah perilaku dasar.


4

Satu keuntungan menjaga kelas tetap final: -

Kelas string tetap final sehingga tidak ada yang bisa mengganti metode dan mengubah fungsionalitasnya. mis. tidak ada yang dapat mengubah fungsionalitas metode length (). Itu akan selalu mengembalikan panjang string.

Pengembang kelas ini tidak ingin ada yang mengubah fungsionalitas kelas ini, jadi ia menjadikannya final.


3

Ya, kadang-kadang Anda mungkin menginginkan ini, baik untuk alasan keamanan atau kecepatan. Ini dilakukan juga di C ++. Ini mungkin tidak yang berlaku untuk program, tetapi lebih jadi untuk kerangka kerja. http://www.glenmccl.com/perfj_025.htm


3

Dalam java final menggunakan kata kunci untuk kesempatan di bawah ini.

  1. Variabel akhir
  2. Metode Terakhir
  3. Kelas Akhir

Dalam java final, variabel tidak bisa menetapkan ulang, kelas akhir tidak bisa diperluas dan metode akhir tidak bisa menimpa.


1

Kelas akhir tidak bisa diperpanjang. Jadi jika Anda ingin kelas berperilaku dengan cara tertentu dan jangan seseorang untuk menimpa metode (dengan kode yang mungkin kurang efisien dan lebih berbahaya), Anda dapat mendeklarasikan seluruh kelas sebagai metode final atau khusus yang Anda tidak ingin menjadi berubah.

Karena mendeklarasikan kelas tidak mencegah kelas menjadi instantiated, itu tidak berarti itu akan menghentikan kelas dari memiliki karakteristik suatu objek. Hanya saja Anda harus tetap berpegang pada metode seperti yang mereka nyatakan di kelas.


1

anggap FINAL sebagai "End of the line" - orang itu tidak dapat menghasilkan keturunan lagi. Jadi ketika Anda melihatnya dengan cara ini, ada banyak skenario dunia nyata yang akan Anda temui yang mengharuskan Anda untuk menandai penanda 'ujung jalur' ke kelas. Ini adalah Domain Driven Design - jika domain Anda menuntut ENTITY (kelas) yang diberikan tidak dapat membuat sub-kelas, maka tandai sebagai FINAL.

Saya harus mencatat bahwa tidak ada yang menghentikan Anda dari mewarisi kelas "harus ditandai sebagai final". Tapi itu umumnya diklasifikasikan sebagai "penyalahgunaan warisan", dan dilakukan karena paling sering Anda ingin mewarisi beberapa fungsi dari kelas dasar di kelas Anda.

Pendekatan terbaik adalah dengan melihat domain dan membiarkannya menentukan keputusan desain Anda.


1

Seperti yang dikatakan di atas, jika Anda ingin tidak ada yang dapat mengubah fungsionalitas metode ini maka Anda dapat mendeklarasikannya sebagai final.

Contoh: Jalur file server aplikasi untuk mengunduh / mengunggah, memisahkan string berdasarkan offset, metode seperti itu Anda dapat mendeklarasikannya Final sehingga fungsi metode ini tidak akan diubah. Dan jika Anda ingin metode akhir seperti itu di kelas yang terpisah, maka tentukan kelas itu sebagai kelas Final. Jadi kelas Final akan memiliki semua metode final, dimana metode Final dapat dideklarasikan dan didefinisikan dalam kelas non-final.



1

Katakanlah Anda memiliki Employeekelas yang memiliki metode greet. Ketika greetmetode ini disebut hanya mencetak Hello everyone!. Jadi itu adalah perilaku yang diharapkan dari greetmetode

public class Employee {

    void greet() {
        System.out.println("Hello everyone!");
    }
}

Sekarang, biarkan metode GrumpyEmployeesubclass Employeedan override greetseperti yang ditunjukkan di bawah ini.

public class GrumpyEmployee extends Employee {

    @Override
    void greet() {
        System.out.println("Get lost!");
    }
}

Sekarang dalam kode di bawah ini lihat sayHellometode. Dibutuhkan Employeeinstance sebagai parameter dan memanggil metode sapaan berharap itu akan mengatakan Hello everyone!Tapi apa yang kita dapatkan adalah Get lost!. Perubahan perilaku ini karenaEmployee grumpyEmployee = new GrumpyEmployee();

public class TestFinal {
    static Employee grumpyEmployee = new GrumpyEmployee();

    public static void main(String[] args) {
        TestFinal testFinal = new TestFinal();
        testFinal.sayHello(grumpyEmployee);
    }

    private void sayHello(Employee employee) {
        employee.greet(); //Here you would expect a warm greeting, but what you get is "Get lost!"
    }
}

Situasi ini dapat dihindari jika Employeekelas dibuat final. Bayangkan saja jumlah kekacauan yang bisa disebabkan oleh programmer yang nakal jika StringKelas tidak dinyatakan sebagai final.


1

Kelas akhir tidak bisa diperpanjang. Jika kita tidak perlu membuat kelas bisa diwarisi dalam java, kita bisa menggunakan pendekatan ini.

Jika kita hanya perlu membuat metode tertentu di kelas agar tidak ditimpa, kita hanya bisa meletakkan kata kunci terakhir di depannya. Di sana kelasnya masih bisa diwariskan.


-1

Orientasi Objek bukan tentang warisan, ini tentang enkapsulasi. Dan warisan merusak enkapsulasi.

Mendeklarasikan final kelas sangat masuk akal dalam banyak kasus. Objek apa pun yang mewakili "nilai" seperti warna atau jumlah uang bisa final. Mereka berdiri sendiri.

Jika Anda menulis perpustakaan, buat kelas Anda final kecuali Anda secara eksplisit membuat mereka berasal. Kalau tidak, orang dapat menurunkan kelas Anda dan mengganti metode, melanggar asumsi / invarian Anda. Ini mungkin memiliki implikasi keamanan juga.

Joshua Bloch dalam “Java Efektif” merekomendasikan untuk mendesain secara eksplisit untuk pewarisan atau melarangnya dan ia mencatat bahwa mendesain untuk pewarisan tidak semudah itu.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.