Mengapa Java tidak mengizinkan definisi fungsi hadir di luar kelas?

22

Tidak seperti C ++, di Java, kita tidak bisa hanya memiliki fungsi deklarasi di kelas dan definisi di luar kelas. Kenapa gitu?

Apakah ini untuk menekankan bahwa satu file di Jawa hanya boleh berisi satu kelas dan tidak ada yang lain?

agas
sumber
1
Menurut definisi yang Anda maksud properti, atau tanda tangan metode ala file header?
Deco
Jawaban satir yang bagus untuk pertanyaan Anda: steve-yegge.blogspot.com/2006/03/…
Brandon

Jawaban:

33

Perbedaan antara C ++ dan Java adalah dalam hal bahasa menganggap unit keterkaitan terkecil mereka.

Karena C dirancang untuk hidup berdampingan dengan perakitan, unit itu adalah subrutin yang dipanggil oleh alamat. (Ini berlaku untuk bahasa lain yang dikompilasi ke file objek asli, seperti FORTRAN.) Dengan kata lain, file objek yang berisi fungsi foo()akan memiliki simbol yang dipanggil _fooyang tidak akan diselesaikan dengan alamat selain alamat seperti0xdeadbeefselama menghubungkan. Itu saja yang ada. Jika fungsinya adalah untuk mengambil argumen, itu tergantung pada pemanggil untuk memastikan semua fungsi diharapkan dalam urutan sebelum memanggil alamatnya. Biasanya, ini dilakukan dengan menumpuk benda-benda ke tumpukan, dan kompiler menangani pekerjaan kasar dan memastikan prototipe cocok. Tidak ada pemeriksaan ini di antara file objek; jika Anda melakukan kesalahan pada tautan panggilan, panggilan itu tidak akan berjalan sesuai rencana dan Anda tidak akan mendapat peringatan tentang hal itu. Meskipun ada bahaya, ini memungkinkan file objek yang dikompilasi dari beberapa bahasa (termasuk assembly) untuk dihubungkan bersama ke dalam program yang berfungsi tanpa banyak keributan.

C ++, terlepas dari semua kesenangan tambahannya, bekerja dengan cara yang sama. Compiler shoehorns namespaces, kelas dan metode / anggota / dll. ke dalam konvensi ini dengan meratakan isi kelas menjadi satu nama yang dibuat sedemikian rupa sehingga membuatnya unik. Sebagai contoh, metode seperti Foo::bar(int baz)mungkin bisa hancur _ZN4Foo4barEiketika dimasukkan ke dalam file objek dan alamat seperti 0xBADCAFEsaat runtime. Ini sepenuhnya tergantung pada kompiler, jadi jika Anda mencoba untuk menghubungkan dua objek yang memiliki skema mangling yang berbeda, Anda akan kurang beruntung. Jelek seperti ini, itu berarti Anda dapat menggunakan extern "C"blok untuk menonaktifkan mangling, memungkinkan untuk membuat kode C ++ mudah diakses oleh bahasa lain. C ++ mewarisi gagasan fungsi mengambang bebas dari C, terutama karena format objek asli memungkinkannya.

Java adalah binatang yang berbeda yang hidup di dunia yang terisolasi dengan format file objeknya sendiri, .classfile tersebut. File kelas berisi banyak informasi tentang konten mereka yang memungkinkan lingkungan melakukan hal-hal dengan kelas saat runtime yang bahkan tidak bisa diimpikan oleh mekanisme tautan asli. Informasi itu harus dimulai di suatu tempat, dan titik awal itu adalahclass. Informasi yang tersedia memungkinkan kode yang dikompilasi untuk menggambarkan dirinya sendiri tanpa perlu file terpisah yang berisi deskripsi dalam kode sumber seperti yang Anda miliki dalam C, C ++ atau bahasa lainnya. Itu memberi Anda semua jenis bahasa manfaat keamanan menggunakan kekurangan linkage asli, bahkan saat runtime, dan itulah yang memungkinkan Anda untuk memancing kelas sewenang-wenang dari file menggunakan refleksi dan menggunakannya dengan kegagalan dijamin jika sesuatu tidak cocok .

Jika Anda belum mengetahuinya, semua keamanan ini disertai dengan pengorbanan: apa pun yang Anda tautkan ke program Java haruslah Java. (Dengan "tautan," Maksud saya kapan saja sesuatu dalam satu file kelas mengacu pada sesuatu di file lain.) Anda dapat menautkan (dalam pengertian asli) ke kode asli menggunakan JNI , tetapi ada kontrak implisit yang mengatakan bahwa jika Anda melanggar sisi asli , Anda memiliki kedua bagian.

Java besar dan tidak terlalu cepat pada perangkat keras yang tersedia ketika pertama kali diperkenalkan, seperti Ada pada dekade sebelumnya. Hanya Jim Gosling yang dapat mengatakan dengan pasti apa motivasinya dalam membuat unit hubungan terkecil di kelasnya di Jawa, tetapi saya harus menebak bahwa kompleksitas tambahan yang ditambahkan dengan pelampung bebas akan ditambahkan ke runtime mungkin adalah pembunuh bayaran.

Blrfl
sumber
tautan rusak, dapatkan tautan arsip web
noɥʇʎԀʎzɐɹƆ
14

Saya percaya jawabannya adalah, per Wikipedia, bahwa Java dirancang untuk menjadi sederhana dan berorientasi objek. Fungsi dimaksudkan untuk beroperasi di kelas tempat mereka didefinisikan. Dengan garis pemikiran itu, memiliki fungsi di luar kelas tidak masuk akal. Saya akan melompat ke kesimpulan bahwa Java tidak mengizinkannya karena tidak cocok dengan OOP murni.

Pencarian Google cepat untuk saya tidak menghasilkan banyak motivasi desain bahasa Jawa.

mortalapeman
sumber
6
Bukankah keberadaan tipe-tipe primitif mengecualikan Jawa dari menjadi "OOP murni"?
Radu Murzea
5
@ SoboLAN: Ya, dan menambahkan fungsi akan membuatnya semakin kurang "pure OOP".
Giorgio
8
@SoboLAN yang tergantung pada definisi Anda tentang "OOP murni". Pada titik tertentu semuanya adalah kombinasi dari bit dalam memori komputer ...
jwenting
3
@ jwenting: Ya, tujuan abstraksi dalam bahasa pemrograman adalah untuk menyembunyikan bit yang mendasarinya sebanyak mungkin. Semakin banyak Anda dapat melihat bit-bit itu di program Anda, semakin Anda harus mulai berpikir bahwa bahasa pemrograman Anda menawarkan abstraksi yang bocor (kecuali jika sengaja dibuat dekat dengan logam). Jadi, ya, semuanya bermuara pada memanipulasi bit, tetapi bahasa yang berbeda menawarkan tingkat abstraksi yang berbeda, jika tidak, Anda tidak akan dapat membedakan rakitan dari bahasa tingkat yang lebih tinggi.
Giorgio
2
Tergantung pada definisi Anda tentang "OOP murni": setiap nilai data adalah objek, dan setiap operasi data adalah pemanggilan metode hasil yang diperoleh melalui pengiriman pesan. Sejauh yang saya tahu tidak ada yang lebih dari itu.
Giorgio
11

Pertanyaan sebenarnya adalah apa yang layak untuk terus melakukan hal-hal dengan cara C ++ dan apa tujuan asli dari file header? Jawaban singkatnya adalah bahwa gaya file header memungkinkan waktu kompilasi yang lebih cepat pada proyek-proyek besar di mana banyak kelas berpotensi merujuk tipe yang sama. Ini tidak perlu di JAVA dan .NET karena sifat dari kompiler.

Lihat jawaban ini di sini: Apakah file header benar-benar bagus?

Jonathan Henson
sumber
1
+1, meskipun saya benar-benar mendengar orang mengatakan mereka suka pemisahan antara antarmuka publik dan implementasi pribadi yang disediakan file header. Orang-orang itu salah, tentu saja. ;)
vaughandroid
6
@ Baqueta Di Jawa Anda dapat mencapai ini dengan interfacedan class:) Tidak perlu header!
Andres F.
1
Saya tidak akan pernah mengerti keinginan untuk melihat 3+ file untuk memahami bagaimana instance kelas sederhana bekerja.
Erik Reppen
@ErikReppen biasanya, antarmuka (atau file header dalam C) adalah hal yang pelanggan dapatkan dalam bentuk yang dapat dibaca pengguna untuk menulis solusi mereka, sisanya disediakan dalam bentuk biner saja (tentu saja di Jawa, tidak perlu menyediakan sumber-sumber) dari interface, classfiles dan javadoc akan melakukan).
jwenting
@wenting aku belum memikirkan kasus itu. Saya lebih terbiasa memikirkan bajingan malang berikutnya yang harus mempertahankan basis kode konyol ini, karena terlalu sering itu saya di pekerjaan berikutnya menusuk mata saya setelah menghabiskan berjam-jam melihat beberapa jenis antarmuka yang aneh dan sub / superclass merry arsitektur -berputar.
Erik Reppen
3

File Java mewakili kelas. Jika Anda memiliki prosedur di luar kelas, apa ruang lingkupnya? Apakah ini bersifat global? Atau apakah itu milik kelas yang diwakili oleh file Java?

Agaknya, Anda memasukkannya ke dalam file Java alih-alih file lain karena suatu alasan - karena itu berlaku untuk kelas itu lebih dari kelas lainnya. Jika prosedur di luar kelas sebenarnya terkait dengan kelas itu, lalu mengapa tidak memaksanya untuk masuk ke dalam kelas itu di mana tempatnya? Java menangani ini sebagai metode statis di dalam kelas.

Jika prosedur kelas luar diizinkan, mungkin tidak akan memiliki akses khusus ke kelas yang filenya dideklarasikan, sehingga membatasi ke fungsi utilitas yang tidak mengubah data apa pun.

Satu-satunya kemungkinan sisi negatif dari batasan Java ini adalah bahwa jika Anda benar-benar memiliki prosedur global yang tidak terkait dengan kelas apa pun, Anda akhirnya membuat kelas MyGlobals untuk menampungnya, dan mengimpor kelas itu di semua file Anda yang menggunakan prosedur tersebut. .

Bahkan, mekanisme impor Jawa membutuhkan pembatasan ini agar berfungsi. Dengan semua API yang tersedia, kompiler java perlu tahu persis apa yang harus dikompilasi dan apa yang harus dikompilasi, sehingga pernyataan impor eksplisit di bagian atas file. Tanpa harus mengelompokkan global Anda ke dalam kelas buatan, bagaimana Anda akan memberitahu kompiler Java untuk mengkompilasi global Anda dan tidak semua dan semua global pada classpath Anda? Bagaimana dengan tabrakan namespace di mana Anda memiliki doStuff () dan orang lain memiliki doStuff ()? Itu tidak akan berhasil. Memaksa Anda menentukan MyClass.doStuff () dan YourClass.doStuff () memperbaiki masalah ini. Memaksa prosedur Anda untuk masuk ke dalam MyClass alih-alih di luar hanya memperjelas batasan ini dan tidak memberlakukan batasan tambahan pada kode Anda.

Java mendapat beberapa hal yang salah - serialisasi memiliki banyak kutil kecil sehingga hampir terlalu sulit untuk berguna (pikirkan SerialVersionUID). Ini juga dapat digunakan untuk memecahkan lajang dan pola desain umum lainnya. Metode clone () pada Object harus dibagi menjadi deepClone () dan shallowClone () dan menjadi tipe-safe. Semua kelas API bisa dibuat tidak dapat diubah secara default (seperti di Scala). Tetapi pembatasan bahwa semua prosedur harus dimiliki kelas adalah bagus. Ini berfungsi terutama untuk menyederhanakan dan mengklarifikasi bahasa dan kode Anda tanpa memaksakan pembatasan berat.

GlenPeterson
sumber
3

Saya pikir sebagian besar orang yang menjawab dan pemilih mereka telah salah paham tentang pertanyaan itu. Ini mencerminkan bahwa mereka tidak tahu C ++.

"Definisi" dan "Deklarasi" adalah kata-kata dengan makna yang sangat spesifik dalam C ++.

OP tidak berarti mengubah cara kerja Java. Ini adalah pertanyaan murni tentang sintaksis. Saya pikir ini adalah pertanyaan yang valid.

Di C ++ ada dua cara untuk mendefinisikan fungsi anggota.

Cara pertama adalah cara Java. Masukkan semua kode di dalam kurung:

class Box {
public:
    // definition of member function
    void change(int newInt) { 
        this._m = newInt;
    }
private:
    int _m
}

Cara kedua:

class Box {
public:  
    // declaration of member function
    void change(int newInt); 
private:
    int _m
}

// definition of member function
// this can be in the same file as the declaration
void Box::change(int newInt) {
    this._m = newInt;
}

Kedua program itu sama. Fungsi changeini masih merupakan fungsi anggota: tidak ada di luar kelas. Selanjutnya definisi kelas HARUS menyertakan nama dan jenis fungsi SEMUA anggota dan variabel, seperti di Jawa.

Jonathan Henson benar bahwa ini adalah artefak dari cara kerja header di C ++: ini memungkinkan Anda untuk menempatkan deklarasi dalam file header dan implementasi dalam file .cpp terpisah sehingga program Anda tidak melanggar ODR (Aturan Satu Definisi). Tetapi memiliki kelebihan di luar itu: itu memungkinkan Anda untuk melihat antarmuka kelas besar sekilas.

Di Java Anda bisa memperkirakan efek ini dengan kelas atau antarmuka abstrak, tetapi mereka tidak bisa memiliki nama yang sama dengan kelas implementasi, yang membuatnya agak canggung.

Erik van Velzen
sumber
dan itu menunjukkan Anda berdua tidak mengerti orang maupun Jawa. Anda dapat menggunakan nama yang sama untuk antarmuka dan implementasi, asalkan tidak ada dalam paket yang sama.
jwenting
Saya menganggap paket (atau namespace, atau kelas luar) sebagai bagian dari nama.
Erik van Velzen
2

Saya pikir ini adalah artefak dari mekanisme pemuatan kelas. Setiap file kelas adalah wadah untuk objek yang dapat dimuat. Tidak ada tempat "di luar" dari file kelas.

ddyer
sumber
1
Saya tidak melihat mengapa mengatur kode sumber di unit yang terpisah akan mencegah menjaga format file kelas apa adanya.
Mat
ada korespondensi 1: 1 antara file kelas dan file sumber, yang merupakan salah satu keputusan desain yang lebih baik dalam sistem keseluruhan.
ddyer
@ddyer Anda belum pernah melihat Foo $ 2 $ 1.kelas lalu? (lihat Java, nama file kelas dalam kelas )
Apakah itu menjadikannya pemetaan "ke"? dalam setiap kasus, setiap kelas dihasilkan dari kompilasi tepat satu file sumber, yang merupakan keputusan desain yang bagus.
ddyer
0

C #, yang sangat mirip dengan Java, memang memiliki fitur semacam ini melalui penggunaan metode parsial, kecuali bahwa metode parsial secara eksklusif bersifat pribadi.

Metode Sebagian: http://msdn.microsoft.com/en-us/library/6b0scde8.aspx

Kelas dan Metode Parsial: http://msdn.microsoft.com/en-us/library/wa80x488.aspx

Saya tidak melihat alasan mengapa Java tidak bisa melakukan hal yang sama, tetapi mungkin hanya turun ke apakah ada kebutuhan yang dirasakan dari basis pengguna untuk menambahkan fitur ini ke bahasa.

Sebagian besar alat pembuatan kode untuk C # menghasilkan kelas parsial sehingga pengembang dapat dengan mudah menambahkan kode yang ditulis secara manual ke kelas dalam file terpisah, jika diinginkan.

Apprentice Dr. Wily
sumber
0

C ++ mengharuskan seluruh teks kelas dikompilasi sebagai bagian dari setiap unit kompilasi yang menggunakan anggota mana pun darinya atau menghasilkan instance apa pun. Satu-satunya cara agar waktu kompilasi tetap waras adalah dengan membuat teks kelas itu sendiri mengandung - sejauh mungkin - hanya hal-hal yang sebenarnya perlu bagi konsumennya. Fakta bahwa metode C ++ sering ditulis di luar kelas yang mengandungnya adalah hack jahat yang termotivasi oleh fakta bahwa membutuhkan kompiler untuk memproses teks dari setiap metode kelas sekali untuk setiap unit kompilasi di mana kelas yang digunakan akan mengarah ke total kali membangun gila.

Di Jawa, file kelas terkompilasi berisi, antara lain, informasi yang sebagian besar setara dengan file C ++ .h. Konsumen kelas dapat mengekstraksi semua informasi yang mereka butuhkan dari file itu, tanpa harus memiliki proses kompilator file .java. Tidak seperti C ++, di mana file .h berisi informasi yang tersedia untuk implementasi dan klien dari kelas-kelas yang terkandung di dalamnya, aliran di Jawa dibalik: file yang digunakan klien bukanlah file sumber yang digunakan dalam kompilasi kode kelas, tetapi dihasilkan oleh kompiler menggunakan informasi file kode kelas. Karena tidak perlu membagi kode kelas antara file yang berisi informasi yang dibutuhkan klien dan file yang berisi implementasi, Java tidak mengizinkan pemisahan seperti itu.

supercat
sumber
-1

Saya pikir bagian dari itu adalah bahwa Jawa sangat banyak bahasa proteksionis yang berkaitan dengan penggunaan pada tim besar. Kelas tidak dapat ditimpa atau didefinisikan ulang. Anda memiliki 4 level pengubah akses yang mendefinisikan secara spesifik bagaimana metode dapat dan tidak dapat digunakan. Semuanya kuat / diketik secara statis untuk melindungi dev dari tipe ketidakcocokan hijinx yang disebabkan oleh orang lain atau diri mereka sendiri. Memiliki kelas dan fungsi sebagai unit terkecil Anda membuatnya jauh lebih mudah untuk menemukan kembali paradigma tentang cara membuat aplikasi.

Bandingkan dengan JavaScript di mana fungsi kelas satu kata benda / kata kerja bertujuan hujan turun dan diedarkan seperti permen dari pinata terbuka, konstruktor fungsi setara kelas dapat mengubah prototipe menambahkan properti baru atau mengubah yang lama untuk contoh yang telah dibuat kapan saja, sama sekali tidak ada yang menghentikan Anda untuk mengganti konstruksi apa pun dengan versi Anda sendiri, ada 5 jenis dan mereka secara otomatis mengkonversi dan mengevaluasi secara otomatis dalam keadaan yang berbeda dan Anda dapat melampirkan properti baru di dekat apa pun:

function nounAndVerb(){
}
nounAndVerb.newProperty = 'egads!';

Pada akhirnya ini tentang ceruk dan pasar. JavaScript kira-kira sesuai dan mendatangkan malapetaka di tangan 100 (banyak di antaranya mungkin akan menjadi biasa-biasa saja) seperti halnya Jawa pernah berada di tangan sekelompok kecil pengembang yang mencoba menulis UI web dengannya. Ketika Anda bekerja dengan 100 devs, hal terakhir yang Anda inginkan adalah satu orang menemukan kembali paradigma terkutuk itu. Ketika Anda bekerja dengan UI atau pengembangan cepat adalah masalah yang lebih kritis, hal terakhir yang Anda inginkan adalah dihadang jalan untuk melakukan hal-hal yang relatif sederhana dengan cepat hanya karena akan mudah untuk melakukannya dengan sangat bodoh jika Anda tidak hati-hati.

Namun pada akhirnya, mereka berdua populer menggunakan bahasa umum sehingga ada sedikit argumen filosofis di sana. Daging sapi pribadi terbesar saya dengan Java dan C # adalah bahwa saya belum pernah melihat atau bekerja dengan basis kode warisan di mana mayoritas devs tampaknya telah memahami nilai OOP dasar. Ketika Anda datang ke permainan harus membungkus segala sesuatu dalam satu kelas, mungkin lebih mudah untuk menganggap Anda melakukan OOP daripada fungsi spaghetti yang disamarkan sebagai rantai besar dari 3-5 kelas garis.

Yang mengatakan, tidak ada yang lebih mengerikan dari JavaScript yang ditulis oleh seseorang yang hanya tahu cukup untuk menjadi berbahaya dan tidak takut untuk memamerkannya. Dan saya pikir itu ide umum. Orang itu seharusnya harus bermain dengan aturan yang kurang lebih sama di Jawa. Saya berpendapat bahwa bajingan itu akan selalu menemukan jalan.

Erik Reppen
sumber