Metode `final` Java: apa yang dijanjikannya?

141

Di kelas Java metode dapat didefinisikan final, untuk menandai bahwa metode ini mungkin tidak diganti:

public class Thingy {
    public Thingy() { ... }
    public int operationA() {...}
    /** this method does @return That and is final. */
    public final int getThat() { ...}
}

Itu jelas, dan mungkin berguna untuk melindungi dari pengesampingan yang tidak disengaja, atau mungkin kinerja - tapi itu bukan pertanyaan saya.

Pertanyaan saya adalah: Dari sudut pandang OOP saya mengerti bahwa, dengan mendefinisikan suatu metode final, perancang kelas menjanjikan metode ini akan selalu berfungsi seperti yang dijelaskan, atau tersirat. Namun seringkali ini mungkin berada di luar pengaruh penulis kelas, jika apa yang dilakukan metode ini lebih rumit daripada hanya memberikan properti .

Batasan sintaksis jelas bagi saya, tetapi apa implikasinya dalam arti OOP? Apakah finaldigunakan dengan benar dalam pengertian ini oleh sebagian besar penulis kelas?

"Kontrak" apa yang finaldijanjikan metode?

towi
sumber

Jawaban:

155

Seperti disebutkan, finaldigunakan dengan metode Java untuk menandai bahwa metode tidak dapat diganti (untuk cakupan objek) atau disembunyikan (untuk statis). Ini memungkinkan pengembang asli untuk membuat fungsionalitas yang tidak dapat diubah oleh subclass, dan hanya itu jaminan yang disediakannya.

Ini berarti bahwa jika metode ini bergantung pada komponen yang dapat disesuaikan lainnya seperti bidang / metode non-publik, fungsionalitas metode terakhir mungkin masih dapat disesuaikan. Ini bagus meskipun (dengan polimorfisme) memungkinkan penyesuaian sebagian.

Ada sejumlah alasan untuk mencegah sesuatu dikustomisasi, termasuk:

  • Kinerja - Beberapa penyusun dapat menganalisis dan mengoptimalkan operasi, terutama yang tanpa efek samping.

  • Dapatkan data yang dienkapsulasi - lihat Objek yang tidak berubah di mana atributnya ditetapkan pada waktu konstruksi dan jangan pernah diubah. Atau nilai yang dihitung berasal dari atribut tersebut. Contoh yang bagus adalah Stringkelas Java .

  • Keandalan dan Kontrak - Objek terdiri dari primitif ( int, char, double, dll) dan / atau Objek lainnya. Tidak semua operasi yang berlaku untuk komponen-komponen itu harus dapat diterapkan atau bahkan logis ketika digunakan dalam Objek yang lebih besar. Metode dengan finalpengubah dapat digunakan untuk memastikan hal itu. Kelas Counter adalah contoh yang bagus.


public class Counter {
    private int counter = 0;

    public final int count() {
        return counter++;
    }

    public final int reset() {
        return (counter = 0);
    }
}

Jika public final int count()metode ini tidak final, kita dapat melakukan sesuatu seperti ini:

Counter c = new Counter() {   
    public int count() {
        super.count();   
        return super.count();   
    } 
}

c.count(); // now count 2

Atau sesuatu seperti ini:

Counter c = new Counter() {
    public int count() {
        int lastCount = 0;
        for (int i = super.count(); --i >= 0; ) {
            lastCount = super.count();
        }

        return lastCount;
    }
}

c.count(); // Now double count
NawaMan
sumber
27

"Kontrak" macam apa yang dijanjikan metode akhir?

Lihatlah dengan cara lain, metode non final mana pun membuat jaminan implisit bahwa Anda dapat menimpanya dengan implementasi Anda sendiri dan kelas akan tetap bekerja seperti yang diharapkan. Ketika Anda tidak dapat menjamin bahwa kelas Anda mendukung menimpa metode, Anda harus membuatnya final.

Josefx
sumber
Tetapi apakah pandangan ini tidak menyiratkan pertanyaan awal saya "metode terakhir ini akan selalu berlaku seperti yang dijanjikan" bahwa saya tidak boleh memanggil metode non-final dari dalam metode final? Karena, jika saya melakukan metode yang dipanggil mungkin telah ditimpa dan karena itu saya tidak dapat menjamin perilaku metode terakhir saya?
towi
8

Pertama-tama, Anda dapat menandai kelas non-abstrak finalserta bidang dan metode. Dengan cara ini seluruh kelas tidak dapat disubklasifikasikan. Jadi, perilaku kelas akan diperbaiki.

Saya setuju bahwa metode penandaan finaltidak menjamin bahwa perilaku mereka akan sama dalam subkelas jika metode ini memanggil metode non-final. Jika perilaku memang perlu diperbaiki, ini harus dicapai dengan konvensi dan desain yang cermat. Dan jangan lupa untuk mengatakan ini di javadoc! (Dokumentasi java)

Terakhir, finalkata kunci memiliki peran yang sangat penting dalam Java Memory Model (JMM). Dijamin oleh JMM bahwa untuk mencapai visibilitas finalbidang Anda tidak perlu sinkronisasi yang tepat. Misalnya:

class A implements Runnable {
  final String caption = "Some caption";                           

  void run() {
    // no need to synchronize here to see proper value of final field..
    System.out.println(caption);
  }
}  
Victor Sorokin
sumber
ya saya tahu tentang kelas akhir - kasus mudah. Poin bagus tentang bidang terakhir dengan JMM, tidak perlu sinkronisasi ... hmm: ini hanya mengacu pada "pointer", kan? Saya masih bisa memodifikasi objek yang dirujuknya tidak sinkron (ok, bukan di String, tetapi di kelas yang ditentukan pengguna). Tapi apa yang Anda katakan tentang "akhir tidak menjamin perilaku" adalah tepat poin saya. Saya setuju, dok dan desain itu penting.
towi
@towi Anda benar yang finaltidak akan memastikan visibilitas perubahan yang dibuat ke objek majemuk, seperti Map.
Victor Sorokin
0

Saya tidak yakin Anda dapat membuat pernyataan tentang penggunaan "final" dan bagaimana hal itu berdampak pada kontrak desain keseluruhan perangkat lunak. Anda dijamin bahwa tidak ada pengembang yang dapat mengganti metode ini dan membatalkan kontraknya dengan cara itu. Tapi di sisi lain, metode akhir mungkin mengandalkan variabel kelas atau contoh yang nilainya ditetapkan oleh subclass, dan dapat memanggil metode kelas lain yang sedang ditimpa. Jadi final paling tidak merupakan jaminan yang sangat lemah.

Jim Ferrans
sumber
1
Ya, itulah yang saya maksud. Baik. Saya suka istilah "jaminan lemah" :-) Dan saya (kebanyakan) suka C ++ const. Seperti di char const * const = "Hello"atau char const * const addName(char const * const name) const...
towi
0

Tidak, itu tidak di luar pengaruh penulis kelas. Anda tidak bisa menimpanya di kelas turunan Anda, karena itu ia akan melakukan apa yang dimaksudkan oleh kelas dasar penulis.

http://download.oracle.com/javase/tutorial/java/IandI/final.html

Perlu dicatat adalah bagian di mana ia menyarankan bahwa metode yang dipanggil dari konstruktor harus final.

Brian Roach
sumber
1
Yah, secara teknis itu akan melakukan apa yang ditulis oleh penulis kelas dasar .
Joey