Mengapa metode utama statis di Java dan C #, daripada konstruktor?

54

Saya Mencari jawaban pasti dari sumber primer atau sekunder untuk alasan (terutama) Java dan C # memutuskan untuk memiliki metode statis sebagai titik masuk mereka, daripada mewakili contoh aplikasi dengan turunan dari sebuah Applicationkelas (dengan titik masuknya menjadi konstruktor yang tepat).


Latar belakang dan detail penelitian saya sebelumnya

Ini telah ditanyakan sebelumnya. Sayangnya, jawaban yang ada hanya mengemis pertanyaan . Secara khusus, jawaban berikut tidak memuaskan saya, karena saya anggap salah:

  • Akan ada ambiguitas jika konstruktor kelebihan beban. - Faktanya, C # (dan juga C dan C ++) memungkinkan tanda tangan yang berbeda Mainsehingga ambiguitas potensial yang sama ada, dan ditangani.
  • Sebuah staticmetode berarti tidak ada benda-benda dapat dipakai sebelum jadi urutan inisialisasi jelas. - Ini hanya faktual salah, beberapa objek yang dipakai sebelumnya (misalnya dalam konstruktor statis).
  • Jadi mereka dapat dipanggil oleh runtime tanpa harus instantiate objek induk. - Ini bukan jawaban sama sekali.

Sekedar alasan mengapa saya pikir ini adalah pertanyaan yang valid dan menarik:

  • Banyak kerangka kerja yang menggunakan kelas untuk mewakili aplikasi, dan konstruktor sebagai titik masuk. Misalnya, kerangka kerja aplikasi VB.NET menggunakan dialog utama khusus (dan konstruktornya) sebagai titik masuk 1 .

  • Baik Java maupun C # secara teknis tidak memerlukan metode utama. Yah, C # butuh satu untuk dikompilasi, tetapi Java bahkan tidak. Dan dalam kedua kasus itu tidak diperlukan untuk eksekusi. Jadi ini sepertinya bukan batasan teknis. Dan, seperti yang saya sebutkan di paragraf pertama, untuk konvensi belaka, tampaknya anehnya tidak sesuai dengan prinsip desain umum Java dan C #.

Untuk menjadi jelas, tidak ada kerugian spesifik untuk memiliki mainmetode statis , itu hanya aneh , yang membuat saya bertanya-tanya apakah ada beberapa alasan teknis di baliknya.

Saya tertarik pada jawaban pasti dari sumber primer atau sekunder, bukan spekulasi belaka.


1 Meskipun ada panggilan balik ( Startup) yang dapat mencegat ini.

Konrad Rudolph
sumber
4
@ mjfgates Juga, saya berharap telah menjelaskan bahwa ini bukan hanya "mengapa orang tidak melakukannya seperti yang saya inginkan", dan bahwa saya benar-benar tertarik pada alasannya.
Konrad Rudolph
2
Untuk Jawa saya pikir alasannya sederhana: Ketika mengembangkan Java, mereka tahu bahwa kebanyakan orang yang belajar bahasa akan tahu C / C ++ sebelumnya. Oleh karena itu Java tidak hanya terlihat sangat mirip C / C ++ daripada mengatakan smalltalk, tetapi juga mengambil alih keistimewaan dari C / C ++ (pikirkan saja literal integer literal). Karena c / c ++ keduanya menggunakan metode utama, melakukan hal yang sama untuk java masuk akal dari sudut pandang itu.
Voo
5
@Jarrod Kamu tidak adil. Saya pikir saya telah membuatnya dengan jelas tidak menjadi kata-kata kasar. “Tidak konstruktif”? Bagaimana? Saya secara eksplisit meminta referensi, bukan hanya diskusi liar. Anda tentu saja bebas untuk tidak setuju bahwa ini adalah pertanyaan yang menarik . Tetapi jika pertanyaan semacam ini adalah PL di sini, saya benar-benar gagal untuk melihat apa tujuan Programmer.SE melayani.
Konrad Rudolph
2
Diskusi Meta yang relevan .
yannis
3
Pertanyaan: Jika ini adalah objek aplikasi, Anda tidak perlu dua hal. 1) Seorang konstruktor. 2) Metode pada objek untuk menjalankan aplikasi Anda. Konstruktor harus menyelesaikan agar objek menjadi valid dan dengan demikian dapat dijalankan.
Martin York

Jawaban:

38

TL; DR

Di Jawa, alasannya public static void main(String[] args)adalah itu

  1. Gosling ingin
  2. kode yang ditulis oleh seseorang yang berpengalaman dalam C (bukan di Jawa)
  3. untuk dieksekusi oleh seseorang yang terbiasa menjalankan PostScript di NeWS

http://i.stack.imgur.com/qcmzP.png

 
Untuk C #, alasannya hampir sama untuk berbicara. Perancang bahasa membuat sintaksis titik entri program tetap familier bagi programmer yang datang dari Jawa. Seperti yang dikatakan arsitek C #, Anders Hejlsberg ,

... pendekatan kami dengan C # hanyalah menawarkan alternatif ... ke pemrogram Java ...

 

Versi panjang

berkembang di atas dan didukung dengan referensi yang membosankan.

 

Terminator java Hasta la vista Baby!

VM Spec, 2.17.1 Memulai Mesin Virtual

... Cara di mana kelas awal ditentukan untuk mesin virtual Java berada di luar ruang lingkup spesifikasi ini, tetapi biasanya, di lingkungan host yang menggunakan baris perintah, untuk nama kelas yang sepenuhnya memenuhi syarat untuk ditentukan sebagai argumen baris perintah dan untuk argumen baris perintah selanjutnya yang akan digunakan sebagai string yang akan diberikan sebagai argumen ke metode utama. Misalnya, menggunakan Java 2 SDK Sun untuk Solaris, baris perintah

java Terminator Hasta la vista Baby!

akan memulai mesin virtual Java dengan menjalankan metode utama kelas Terminator(kelas dalam paket yang tidak disebutkan namanya) dan memberikannya array yang berisi empat string "Hasta", "la", "vista", dan "Baby!" ...

... lihat juga: Lampiran: Saya membutuhkan pakaian, sepatu bot, dan sepeda motor Anda

  • Interpretasi saya:
    eksekusi ditargetkan untuk digunakan seperti skrip pada antarmuka baris perintah.

 

sidestep penting

... yang membantu menghindari beberapa jejak palsu dalam penyelidikan kami.

VM Spec, 1.2 Mesin Virtual Java

Mesin virtual Java tidak tahu apa-apa tentang bahasa pemrograman Java ...

Saya perhatikan di atas ketika mempelajari bab sebelumnya - 1.1 Sejarah yang saya pikir bisa membantu (tetapi ternyata tidak berguna).

  • Interpretasi saya:
    eksekusi diatur oleh VM spec saja, yang
    secara eksplisit menyatakan bahwa itu tidak ada hubungannya dengan bahasa Java
    => OK untuk mengabaikan JLS dan apa pun yang terkait dengan bahasa Jawa sama sekali

 

Gosling: kompromi antara bahasa C dan scripting ...

Berdasarkan hal di atas, saya mulai mencari web untuk sejarah JVM . Tidak membantu, terlalu banyak sampah dalam hasil.

Kemudian, saya teringat legenda tentang Gosling dan mempersempit pencarian saya ke sejarah Jling Gosling .

Eureka! Bagaimana Spesifikasi JVM Datang

Dalam keynote ini dari JVM Languages ​​Summit 2008, James Gosling membahas ... penciptaan Java, ... kompromi antara C dan bahasa scripting ...

  • Interpretasi saya:
    pernyataan eksplisit bahwa pada saat penciptaan,
    C dan skrip dianggap sebagai pengaruh yang paling penting.
     
    Sudah terlihat anggukan pada skrip di VM Spec 2.17.1,
    argumen baris perintah cukup menjelaskan String[] args
    tetapi staticdan mainbelum ada di sana, perlu menggali lebih lanjut ...

Catatan saat mengetik ini - menghubungkan C, scripting dan VM Spec 1.2 dengan tidak ada-nya-Jawa - Saya merasa seperti sesuatu yang akrab, sesuatu ... berorientasi objek perlahan berlalu. Pegang tanganku dan terus bergerak. Jangan memperlambat, kita hampir sampai

Slide utama tersedia secara online: 20_Gosling_keynote.pdf , cukup nyaman untuk menyalin poin utama.

    halaman 3

        Prasejarah Jawa
        * Apa yang membentuk pemikiran saya

    halaman 9

        Berita
        * Networked Extensible Window System
        * Sistem jendela berdasarkan scripting ....
          PostScript (!!)

    halaman 16

        Sasaran Besar (namun hening):
          Seberapa dekat saya bisa dengan a
          "scripting" merasa ...

    halaman 19

        Konsep aslinya
        * Apakah semua tentang bangunan
          jaringan benda,
          diatur oleh skrip
          bahasa
        * (Unix shells, AppleScript, ...)

    halaman 20

        A Wolf in Sheeps Clothing
        * Sintaks C untuk membuat pengembang
          nyaman

A-ha! Mari kita lihat lebih dekat pada sintaks C .

Contoh "halo, dunia" ...

main()
{
    printf("hello, world\n");
}

... fungsi bernama utama sedang didefinisikan. Fungsi utama melayani tujuan khusus dalam program C; lingkungan run-time memanggil fungsi utama untuk memulai eksekusi program.

... Fungsi utama sebenarnya memiliki dua argumen, int argcdan char *argv[], masing-masing, yang dapat digunakan untuk menangani argumen baris perintah ...

Apakah kita semakin dekat? kamu bertaruh. Juga patut mengikuti tautan "utama" dari kutipan di atas:

fungsi utama adalah di mana program memulai eksekusi. Ini bertanggung jawab untuk organisasi tingkat tinggi dari fungsionalitas program, dan biasanya memiliki akses ke argumen perintah yang diberikan kepada program ketika dieksekusi.

  • Interpretasi saya:
    Agar nyaman bagi pengembang C, titik masuk program harus main.
    Juga, karena Java membutuhkan metode apapun untuk berada di kelas, Class.mainadalah
    sedekat karena mendapat: doa statis, hanya nama kelas dan dot,
    tidak ada konstruktor menyenangkan - C tahu apa-apa seperti itu.
     
    Ini juga transitif berlaku untuk C #, dengan mempertimbangkan
    gagasan migrasi mudah untuk itu dari Jawa.

Pembaca yang berpikir bahwa titik masuk program yang familier tidak masalah diundang dengan ramah untuk mencari dan memeriksa pertanyaan Stack Overflow di mana orang-orang yang berasal dari Java SE mencoba menulis Hello World untuk Java ME MIDP. Catatan MIDP entry point tidak memiliki mainatau tidak static.

 

Kesimpulan

Berdasarkan di atas saya akan mengatakan itu static, maindan String[] argspada saat Jawa dan C # pilihan yang paling masuk akal untuk menentukan titik masuk program .

 

Lampiran: Saya membutuhkan pakaian, sepatu bot, dan sepeda motor Anda

Harus mengakui, membaca VM Spec 2.17.1 sangat menyenangkan.

... baris perintah

java Terminator Hasta la vista Baby!

akan memulai mesin virtual Java dengan memanggil metode utama kelas Terminator(kelas dalam paket yang tidak disebutkan namanya) dan memberikannya array yang berisi empat string "Hasta", "la", "vista", dan "Baby!".

Kami sekarang menguraikan langkah-langkah yang mungkin dilakukan oleh mesin virtual Terminator, sebagai contoh proses pemuatan, penautan, dan inisialisasi yang dijelaskan lebih lanjut di bagian selanjutnya.

Upaya awal ... menemukan bahwa kelas Terminatortidak dimuat ...

Setelah Terminatordimuat, harus diinisialisasi sebelum main dapat dipanggil, dan suatu tipe (kelas atau antarmuka) harus selalu dihubungkan sebelum diinisialisasi. Menautkan (§2.17.3) melibatkan verifikasi, persiapan, dan (opsional) resolusi ...

Verifikasi (§2.17.3) memeriksa bahwa representasi yang dimuat Terminatorterbentuk dengan baik ...

Resolusi (§2.17.3) adalah proses memeriksa referensi simbolis dari kelas Terminator...

 
Referensi simbolis dari Terminatoroh yeah.

agas
sumber
2
Untuk beberapa alasan saya sulit percaya bahwa "modernitas" adalah kata yang sebenarnya.
someguy
@Songo cerita jawabannya juga seperti film. Ini pertama kali diposting di meta , dalam sebuah diskusi penutupan pertanyaan: "Jika pertanyaan itu akan dibuka kembali, saya mungkin akan menulis jawaban seperti di bawah ini ..." Kemudian digunakan untuk mendukung banding untuk dibuka kembali dan akhirnya pindah ke sini
Agak
16

Itu hanya terasa agak kasar bagi saya. Konstruktor digunakan untuk inisialisasi objek: ia menetapkan objek, yang kemudian digunakan oleh kode yang membuatnya.

Jika Anda meletakkan fungsionalitas penggunaan dasar di dalam konstruktor, dan kemudian tidak pernah benar-benar menggunakan objek yang dibuat konstruktor dalam kode eksternal, maka Anda melanggar prinsip-prinsip OOP. Pada dasarnya, melakukan sesuatu yang sangat aneh tanpa alasan yang jelas.

Mengapa Anda tetap ingin melakukan itu?

Mason Wheeler
sumber
5
Tetapi bukankah "instance aplikasi" secara logis sebuah objek? Mengapa itu kasar? Adapun untuk menggunakan objek - ia memiliki satu tujuan: mewakili aplikasi yang sedang berjalan. Kedengarannya sangat SoC -y bagi saya. "Mengapa kamu ingin melakukan itu?" - Saya hanya tertarik pada alasan untuk keputusan karena saya merasa bertentangan dengan sisa mentalitas.
Konrad Rudolph
7
@KonradRudolph: Konstruktor, seperti pengambil properti, umumnya diharapkan selesai dalam waktu yang terbatas tanpa menunggu beberapa peristiwa asinkron (seperti input pengguna) terjadi. Dimungkinkan untuk memiliki konstruktor yang meluncurkan utas aplikasi utama, tetapi itu menambah tingkat kerumitan yang mungkin tidak diperlukan untuk semua aplikasi. Mengharuskan aplikasi konsol yang hanya mencetak "Hello world" ke output standar harus menelurkan utas tambahan akan konyol. Menggunakan Mainmetode bekerja dengan baik untuk kasus sederhana, dan tidak benar-benar masalah dalam kasus-kasus sulit, jadi mengapa tidak?
supercat
9

Untuk Jawa saya pikir alasannya sederhana: Ketika mengembangkan Jawa, para devs tahu bahwa kebanyakan orang yang belajar bahasa akan mengenal C / C ++ sebelumnya.

Oleh karena itu Java tidak hanya terlihat sangat mirip C / C ++ daripada mengatakan smalltalk, tetapi juga mengambil alih keistimewaan dari C / C ++ (pikirkan saja literal integer literal). Karena c / c ++ keduanya menggunakan metode utama, melakukan hal yang sama untuk java masuk akal dari sudut pandang itu.

Saya cukup yakin saya ingat bloch atau seseorang mengatakan sesuatu di sepanjang baris ini tentang mengapa mereka menambahkan literal integer oktal, saya akan melihat apakah saya dapat menemukan beberapa sumber :)

Voo
sumber
2
Jika terlihat sama dengan C ++ sangat penting untuk Java, mengapa mereka misalnya berubah :menjadi extends? Dan public static void main(String [ ] args)di dalam kelas sangat berbeda dari di int main(int argc, char **argv)luar kelas.
svick
2
@svick Satu kemungkinan: Java memperkenalkan antarmuka dan jelas mereka ingin memisahkan dua konsep (mewarisi antarmuka / kelas) - hanya dengan satu "kata kunci" yang tidak akan berfungsi. Dan "sangat berbeda"? Ini adalah pemetaan terdekat yang mungkin dan sejauh ini saya belum pernah melihat seorang programmer c ++ memiliki pemahaman masalah bahwa metode utama statis adalah titik masuk. Berlawanan dengan yang memiliki kelas yang disebut Aplikasi atau sesuatu yang konstruktornya digunakan, adalah sesuatu yang akan terlihat aneh bagi kebanyakan programmer c ++.
Voo
@svick int di c untuk membatalkan di java harus dengan bagaimana kode kembali dari aplikasi dihasilkan - di java, 0 kecuali System.exit (int) dipanggil. Perubahan parameter berkaitan dengan bagaimana array string dilewatkan dalam setiap bahasa. Semua yang ada di java ada di kelas - tidak ada opsi untuk memilikinya di tempat lain. Mengubah :ke extendsadalah masalah sintaksis dan pada dasarnya sama. Semua lainnya ditentukan oleh bahasa.
@MichaelT Tapi semua itu adalah keputusan desain yang membuat Java berbeda dari C ++. Jadi mengapa menjaga Java sama seperti C ++ menjadi penting dalam kasus ini main(), padahal tampaknya itu tidak cukup penting dalam kasus lain.
svick
@vick Kecuali bahwa itu baik-baik saja untuk tidak mengembalikan apa pun dari main di C juga dan hal-hal sepele seperti itu tidak akan membingungkan siapa pun. Intinya bukan untuk membuat ulang c ++ dan semua kesalahannya tetapi hanya untuk membuat programmer lebih di rumah. Bagaimana menurut Anda seorang programmer C ++ akan lebih mudah membaca: Java atau kode objektif-c? Menurut Anda apa yang akan terlihat lebih jelas bagi seorang programmer C ++ metode utama atau konstruktor dari beberapa kelas sebagai titik masuk?
Voo
6

Nah, ada banyak fungsi utama di luar sana yang menjalankan infinite loop. Konstruktor yang bekerja dengan cara ini (dengan objek yang tidak pernah dikonstruksi) adalah yang tampaknya aneh bagi saya.

Ada begitu banyak hal lucu tentang konsep ini. Logika Anda berjalan di atas objek yang belum lahir, objek yang dilahirkan untuk mati (karena mereka melakukan semua pekerjaan mereka di konstruktor), ...

Bukankah semua efek samping ini akan merusak lebih banyak gerobak OO daripada publik biasa (karena itu perlu diakses oleh yang tidak dikenal) statis (karena tidak ada contoh diperlukan bagi kita untuk memulai) batal main (karena itu adalah titik masuk )?

Untuk titik masuk fungsi yang sederhana, sederhana, agar ada di Jawa, publik dan statis akan secara otomatis diperlukan. Meskipun menjadi metode statis , itu bermuara pada apa yang bisa kita dapatkan lebih dekat dari fungsi polos untuk mencapai apa yang diinginkan: titik masuk sederhana.

Jika Anda tidak akan mengadopsi titik masuk fungsi yang sederhana, sederhana, sebagai titik masuk. Apa selanjutnya yang tampaknya tidak aneh sebagai konstruktor yang tidak dimaksudkan untuk dibangun?

pepper_chico
sumber
1
Saya akan mengatakan masalahnya adalah tidak memiliki fungsi kelas satu. Menempelkan main () di dalam objek (yang tidak dipakai sebelum utama dipanggil) adalah sedikit anti-pola. Mungkin mereka harus memiliki objek "aplikasi" yang dikonstruksi dan menjalankan metode main () non-statis, maka Anda dapat meletakkan inisialisasi startup di konstruktor, dan itu akan terasa jauh lebih baik daripada memiliki metode statis, meskipun top-sederhana = level main () fn juga akan bagus. Main statis adalah sedikit dari semuanya.
gbjbaanb
3

Anda dapat dengan cepat menjalankan beberapa tes mandiri di kelas, selama pengembangan, dengan menempelkan main()di kelas yang Anda coba tes.

Graham Borland
sumber
1
Bagi saya ini mungkin alasan yang paling menarik, karena juga memungkinkan beberapa titik masuk selama pengembangan untuk menguji konfigurasi yang berbeda, dll.
cgull
0

Anda harus mulai dari suatu tempat. Main statis adalah lingkungan eksekusi paling sederhana yang dapat Anda miliki - tidak ada instance apa pun (di luar JVM dan parameter string sederhana) perlu dibuat - sehingga dapat "muncul" dengan minimum keributan (dan kemungkinan rendah) kesalahan pengkodean mencegah startup, dll) dan dapat melakukan hal-hal sederhana tanpa banyak pengaturan lainnya.

Pada dasarnya aplikasi KISS.

[Dan, tentu saja, alasan utamanya adalah: Mengapa tidak?]

Daniel R Hicks
sumber
3
Seperti yang saya katakan, saya tidak yakin dengan itu sama sekali. Benda yang bisa dipakai sebelumnya, dan kode dijalankan sebelumnya. Perlu penawaran dari salah satu pengembang asli untuk meyakinkan saya bahwa inilah alasannya.
Konrad Rudolph
2
Jumlah pekerjaan yang diperlukan untuk membuat instance kelas dari kode C cukup banyak identik dengan memanggil metode statis .. Anda bahkan harus melakukan pemeriksaan yang sama (apakah kelas ada? Baik, apakah itu memiliki konstruktor publik dengan tanda tangan yang tepat? baiklah, lanjutkan).
Voo
Tidak ada objek pengguna yang perlu dibuat. Konstruktor objek tidak dieksekusi. API sangat sederhana. Dan itu yang paling mudah dipahami.
Daniel R Hicks
0

Menurut pemahaman saya, alasan utamanya sederhana. Sun adalah perusahaan Unix yang menjual mesin Unix dan Unix adalah apa yang C "main (args)" konvensi untuk memohon biner .

Selain itu Java secara eksplisit dirancang agar mudah diambil untuk programmer C dan C ++, jadi tidak ada alasan yang baik untuk tidak hanya sekadar mengambil konvensi C.

Pendekatan yang dipilih di mana setiap kelas dapat memiliki metode doa cukup fleksibel, terutama dalam kombinasi dengan Main-Classbaris dalam file MANIFEST.MF di jar yang bisa dijalankan.

agas
sumber
Tentu saja, file jar tidak ditemukan sampai nanti.
Daniel R Hicks
-1

Ini tidak konsisten dengan filosofi OOP bahwa suatu program akan menjadi objek dari sudut pandang proses OS, karena tidak ada cara untuk memiliki lebih dari satu menurut definisi.

Selain itu, konstruktor bukanlah titik masuk dengan cara apa pun.

Sepertinya saya adalah pilihan paling masuk akal untuk memiliki fungsi utama sebagai statis, yang sebenarnya ada di akhir hari. Mengingat arsitektur VM seperti JVM dan CLR, pilihan lain tidak perlu mendorongnya.

Yam Marcovic
sumber
1
Saya pikir Anda salah di sana. Hal ini dimungkinkan untuk memiliki lebih dari satu proses, maka lebih dari satu objek. Kebetulan, ini sepenuhnya setara dengan Runnableobjek instantiating untuk memiliki beberapa utas.
Konrad Rudolph
Suatu proses adalah program yang sedang berjalan, dan Anda hanya dapat memulai proses satu kali melalui satu titik masuk. Utas memiliki titik masuknya sendiri tetapi masih dalam proses yang sama.
Yam Marcovic
1
Seperti saya katakan di bawah jawaban someguy, ini tidak relevan. Yang relevan adalah konsistensi logis . Secara logis, proses direpresentasikan sebagai objek oleh peluncur (OS, JVM, apa pun) dan diinisialisasi.
Konrad Rudolph
@KonradRudolph Benar, tetapi inisialisasi suatu program hanyalah satu bagian dari inisialisasi suatu proses, dan tidak melegitimasi pembuat program.
Yam Marcovic