Mengapa penciptaan instan seperti itu?

17

Saya telah belajar C # selama sekitar enam bulan terakhir dan sekarang saya mempelajari Jawa. Pertanyaan saya adalah tentang pembuatan instance (dalam kedua bahasa, sungguh) dan ini lebih dari: Saya bertanya-tanya mengapa mereka melakukannya dengan cara itu. Ambil contoh ini

Person Bob = new Person();

Apakah ada alasan bahwa objek tersebut ditentukan dua kali? Apakah akan ada something_else Bob = new Person()?

Tampaknya jika saya mengikuti dari konvensi itu akan lebih seperti:

int XIsAnInt;
Person BobIsAPerson;

Atau mungkin salah satunya:

Person() Bob;
new Person Bob;
new Person() Bob;
Bob = new Person();

Saya kira saya ingin tahu apakah ada jawaban yang lebih baik daripada "itu hanya cara dilakukan".

Jason Wohlgemuth
sumber
26
Bagaimana jika Orang adalah subtipe dari LivingThing? Anda bisa menulis LivingThing lt = new Person(). Cari warisan dan antarmuka.
xlecoustillier
2
Person Bobmendeklarasikan variabel tipe "referensi ke Person" yang dipanggil Bob. new Person()menciptakan Personobjek. Referensi, variabel, dan objek adalah tiga hal berbeda!
user253751
5
Apakah Anda kesal dengan redundansi? Lalu mengapa tidak menulis var bob = new Person();?
200_sukses
4
Person Bob();mungkin di C ++ dan artinya hampir sama denganPerson Bob = Person();
user60561
3
@ user60561 tidak, itu menyatakan fungsi tanpa argumen dan mengembalikan Person.
Nikolai

Jawaban:

52

Apakah akan ada something_else Bob = Orang baru ()?

Ya, karena warisan. Jika:

public class StackExchangeMember : Person {}

Kemudian:

Person bob = new StackExchangeMember();
Person sam = new Person();

Bob juga orang, dan tentu saja, dia tidak ingin diperlakukan berbeda dari orang lain.

Selanjutnya, kita bisa memberkahi Bob dengan kekuatan super:

public interface IModerator { }
public class StackOverFlowModerator : StackExchangeMember, IModerator {}

IModerator bob = new StackOverFlowModerator();

Jadi, demi sial, ia tidak akan tahan diperlakukan berbeda dari moderator lainnya. Dan dia suka menyelinap di sekitar forum untuk menjaga semua orang tetap di jalur sementara penyamaran:

StackExchangeMember bob = new StackOverFlowModerator();

Kemudian ketika dia menemukan beberapa poster 1 yang buruk, dia melepaskan jubah tembus pandang dan menerkam.

((StackOverFlowModerator) bob).Smite(sam);

Dan kemudian dia bisa bertindak semua tidak bersalah dan hal-hal sesudahnya:

((Person) bob).ImNotMeanIWasJustInstantiatedThatWay();
radarbob
sumber
20
Ini akan jauh lebih jelas jika Anda menurunkan nama objek.
Lightness Races dengan Monica
38
Contoh spesifikasi yang bagus; contoh warisan yang buruk. Untuk siapa pun yang membaca ini, tolong, jangan mencoba menyelesaikan peran pengguna menggunakan warisan.
Aaronaught
8
@Aaronaught benar. Jangan membuat kelas terpisah untuk berbagai jenis orang. Gunakan bitfield enum.
Cole Johnson
1
@Aaronaught Semuanya baik-baik saja mengatakan apa yang tidak boleh dilakukan, tetapi tidak sangat membantu tanpa mengatakan apa yang harus dilakukan orang.
Pharap
5
@Pharap: Saya telah melakukan hal itu, dalam beberapa pertanyaan lain . Jawaban sederhananya adalah bahwa pengguna (otentikasi / identitas) dan kebijakan keamanan (otorisasi / izin) harus diperlakukan sebagai masalah yang terpisah, dan model standar untuk kebijakan keamanan didasarkan pada peran atau berbasis klaim. Warisan lebih berguna untuk menggambarkan objek yang benar-benar melakukan otentikasi, misalnya implementasi LDAP dan implementasi SQL.
Aaronaught
34

Mari kita ambil baris kode pertama Anda dan memeriksanya.

Person Bob = new Person();

Yang pertama Personadalah spesifikasi tipe. Dalam C #, kita bisa membuang ini hanya dengan mengatakan

var Bob = new Person();

dan kompiler akan menyimpulkan tipe variabel Bob dari panggilan konstruktor Person().

Tetapi Anda mungkin ingin menulis sesuatu seperti ini:

IPerson Bob = new Person();

Di mana Anda tidak memenuhi seluruh kontrak API Orang, tetapi hanya kontrak yang ditentukan oleh antarmuka IPerson.

Robert Harvey
sumber
2
+1: Saya akan melakukan IPersoncontoh dalam kode saya untuk memastikan saya tidak sengaja menggunakan metode pribadi apa pun ketika saya menulis kode yang harus disalin / dapat dilestarikan ke IPersonimplementasi lain .
Cort Ammon - Reinstate Monica
@CortAmmon Saya pikir Anda memiliki kesalahan ketik di sana, jelas Anda maksudkan "bekerja dengan polimorfisme" daripada menyalin / berpastil pada kode itu: D
Benjamin Gruenbaum
@BenjaminGruenbaum yakin, untuk beberapa definisi polimorfisme ;-)
Cort Ammon - Reinstate Monica
@CortAmmon bagaimana Anda secara tidak sengaja memanggil metode pribadi ? Tentunya yang Anda maksud internal?
Cole Johnson
@ColeJohnson juga. Saya menulis bahwa memikirkan kasus tertentu yang saya temukan di tempat privateyang bermakna: metode pabrik yang merupakan bagian dari kelas yang dipakai. Dalam situasi saya, akses ke nilai-nilai pribadi harus menjadi pengecualian, bukan norma. Saya bekerja pada kode yang akan hidup lebih lama dari saya. Jika saya melakukannya dengan cara ini, bukan saja saya tidak mungkin menggunakan metode pribadi sendiri, tetapi ketika pengembang berikutnya menyalin / menempel ini beberapa lusin tempat dan pengembang setelah itu menyalinnya, itu mengurangi peluang bahwa seseorang melihat peluang untuk gunakan metode pribadi sebagai perilaku "normal".
Cort Ammon - Pasang kembali Monica
21
  1. Sintaks ini cukup banyak warisan dari C ++, yang, omong-omong, memiliki keduanya:

    Person Bob;

    dan

    Person *bob = new Bob();

    Yang pertama untuk membuat objek dalam lingkup saat ini, yang kedua untuk membuat pointer ke objek yang dinamis.

  2. Anda pasti bisa something_else Bob = new Person()

    IEnumerable<int> nums = new List<int>(){1,2,3,4}

    Anda melakukan dua hal yang berbeda di sini, menyatakan jenis variabel lokal numsdan Anda mengatakan Anda ingin membuat objek baru dari jenis 'Daftar' dan meletakkannya di sana.

  3. C # jenis setuju dengan Anda, karena sebagian besar waktu jenis variabel identik dengan apa yang Anda masukkan maka:

    var nums = new List<int>();
  4. Dalam beberapa bahasa Anda melakukan yang terbaik untuk menghindari menyatakan jenis variabel seperti dalam F # :

    let list123 = [ 1; 2; 3 ]
AK_
sumber
5
Mungkin lebih akurat untuk mengatakan bahwa contoh kedua Anda membuat pointer ke objek Bob baru. Cara menyimpannya secara teknis merupakan detail implementasi.
Robert Harvey
4
"Yang pertama untuk membuat objek lokal di stack, yang kedua untuk membuat objek di heap." Ya ampun, jangan salah informasi ini lagi.
Lightness Races dengan Monica
@LightnessRacesinOrbit lebih baik?
AK_
1
@AK_ Sementara "dinamis" cukup berarti "tumpukan" (ketika tumpukan adalah model memori yang digunakan oleh platform), "otomatis" sangat berbeda dari "tumpukan." Jika ya new std::pair<int, char>(), maka anggota firstdan secondpasangan memiliki durasi penyimpanan otomatis, tetapi mereka mungkin dialokasikan pada heap (sebagai anggota pairobjek durasi penyimpanan dinamis ).
Pasang kembali Monica
1
@AK_: Ya, nama-nama itu menyiratkan dengan tepat apa artinya, sedangkan omong kosong "tumpukan" vs "tumpukan" ini tidak . Itulah intinya. Bahwa Anda menemukan mereka membingungkan hanya memperkuat kebutuhan bagi kami untuk mengajar mereka, sehingga Anda tidak mengandalkan terminologi yang tidak akurat / salah hanya karena itu akrab!
Lightness Races dengan Monica
3

Ada perbedaan besar antara int xdan Person bob. Sebuah intadalah intmerupakan intdan harus selalu menjadi intdan tidak pernah bisa menjadi apa pun selain sebuah int. Bahkan jika Anda tidak menginisialisasi intketika Anda mendeklarasikannya ( int x;), itu masih merupakan intset ke nilai default.

Ketika Anda menyatakan Person bob, bagaimanapun, ada banyak fleksibilitas untuk apa nama bobsebenarnya merujuk pada waktu tertentu. Itu bisa merujuk ke Person, atau bisa merujuk ke beberapa kelas lain, misalnya Programmer, berasal dari Person; bahkan bisa jadi null, mengacu pada tidak ada objek sama sekali.

Sebagai contoh:

  Person bob   = null;
  Person carol = new Person();
  Person ted   = new Programmer();
  Person alice = personFactory.functionThatReturnsSomeKindOfPersonOrNull();

Para perancang bahasa tentu bisa membuat sintaksis alternatif yang akan menyelesaikan hal yang sama dengan Person carol = new Person()simbol yang lebih sedikit, tetapi mereka masih harus mengijinkan Person carol = new Person() (atau membuat aturan aneh yang membuat salah satu dari empat contoh di atas ilegal). Mereka lebih peduli dengan menjaga bahasa "sederhana" daripada menulis kode yang sangat ringkas. Itu mungkin memengaruhi keputusan mereka untuk tidak memberikan sintaksis alternatif yang lebih pendek, tetapi bagaimanapun juga, itu tidak perlu dan mereka tidak menyediakannya.

David K
sumber
1

Kedua deklarasi bisa berbeda tetapi seringkali sama. Pola yang umum dan direkomendasikan di Jawa terlihat seperti:

List<String> list = new ArrayList<>();
Map<String, Integer> map = new HashMap<>();

Variabel-variabel ini listdan mapdideklarasikan menggunakan antarmuka Listdan Mapsementara kode instantiate implementasi spesifik. Dengan cara ini, sisa kode hanya tergantung pada antarmuka dan mudah untuk memilih kelas implementasi yang berbeda untuk instantiate, seperti TreeMap, karena sisa kode tidak dapat bergantung pada bagian HashMapAPI yang ada di luar Mapantarmuka.

Contoh lain di mana kedua jenis berbeda adalah dalam metode pabrik yang mengambil subclass spesifik untuk instantiate, kemudian mengembalikannya sebagai jenis dasar sehingga penelepon tidak perlu mengetahui detail implementasi, misalnya pilihan "kebijakan".

Ketik inferensi dapat memperbaiki redundansi kode sumber. Misalnya di Jawa

List<String> listOne = Collections.emptyList();

akan membangun jenis Daftar yang benar berkat ketikkan inferensi dan deklarasi

static <T> List<T> emptyList(); 

Dalam beberapa bahasa, ketik inferensi lebih jauh, misalnya dalam C ++

auto p = new Person();
Jerry101
sumber
BTW bahasa Java menetapkan konvensi yang kuat untuk menggunakan nama pengidentifikasi huruf kecil, seperti bob, tidak Bob. Ini menghindari banyak ambiguitas misalnya package.Class vs Class.variable.
Jerry101
... dan itu sebabnya kamu punya clazz.
CVn
Tidak, clazzdigunakan karena classkata kunci sehingga tidak dapat digunakan sebagai pengidentifikasi.
Jerry101
... yang tidak akan menjadi masalah jika konvensi penamaan tidak seperti semula. Classadalah pengidentifikasi yang benar-benar valid.
cHao
... sebagaimana adanya cLaSs, cLASSdan cLASs.
el.pescado
1

Dalam kata-kata awam:

  • Memisahkan deklarasi dari instantiation membantu memisahkan siapa yang menggunakan objek dari siapa yang menciptakannya
  • Ketika Anda melakukannya, poliporfisme diaktifkan sejak, selama tipe instantiated adalah subtipe dari tipe deklarasi, semua kode yang menggunakan variabel akan berfungsi
  • Dalam bahasa yang diketik dengan sangat baik Anda harus mendeklarasikan variabel yang menyatakan jenisnya, dengan melakukan var = new Process()Anda tidak mendeklarasikan variabel terlebih dahulu.
Tulains Córdova
sumber
0

Ini juga tentang tingkat kontrol atas apa yang terjadi. Jika deklarasi objek / variabel secara otomatis memanggil konstruktor, misalnya, jika

Person somePerson;

adalah otomatis sama dengan

Person somePerson = new Person(blah, blah..);

maka Anda tidak akan pernah dapat menggunakan (misalnya) metode pabrik statis untuk membuat instance objek daripada konstruktor default, yaitu, ada kalanya Anda tidak ingin memanggil konstruktor untuk instance objek baru.

Contoh ini dijelaskan di Joshua Bloch 's Jawa Efektif (Item 1 cukup ironis!)

David Scholefield
sumber
Mengenai buku, buku C # dan buku Java saya berasal dari Joyce Farrell. Itulah yang ditentukan kursus. Saya juga telah melengkapi baik dengan berbagai video youtube di C # dan Java.
Jason Wohlgemuth
Saya tidak mengkritik, hanya memberikan referensi dari mana asalnya :)
David Scholefield