เหตุใดการสร้างอินสแตนซ์จึงเป็นแบบนี้


17

ฉันได้เรียนรู้ C # ในช่วงหกเดือนที่ผ่านมาและตอนนี้ฉันกำลังขุดหา Java คำถามของฉันเกี่ยวกับการสร้างอินสแตนซ์ (ในทั้งสองภาษาจริง ๆ ) และมีมากกว่า: ฉันสงสัยว่าทำไมพวกเขาถึงทำแบบนั้น นำตัวอย่างนี้

Person Bob = new Person();

มีเหตุผลที่ระบุวัตถุสองครั้งหรือไม่ เคยมีsomething_else Bob = new Person()บ้างไหม?

ดูเหมือนว่าถ้าฉันทำตามจากการประชุมมันจะเป็นเหมือน:

int XIsAnInt;
Person BobIsAPerson;

หรืออาจเป็นหนึ่งในสิ่งเหล่านี้:

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

ฉันคิดว่าฉันอยากรู้ว่าถ้ามีคำตอบที่ดีกว่า "นั่นเป็นเพียงวิธีที่มันทำ"


26
เกิดอะไรขึ้นถ้าคนที่เป็นชนิดย่อยของLivingThing? LivingThing lt = new Person()คุณสามารถเขียน ค้นหาการสืบทอดและอินเทอร์เฟซ
xlecoustillier

2
Person Bobประกาศตัวแปรประเภท "การอ้างอิงถึงPerson" Bobที่เรียกว่า new Person()สร้างPersonวัตถุ การอ้างอิงตัวแปรและวัตถุต่างกันสามอย่าง!
user253751

5
คุณรู้สึกหงุดหงิดกับความซ้ำซ้อนหรือไม่? ถ้าอย่างนั้นทำไมไม่เขียนvar bob = new Person();?
200_success

4
Person Bob();เป็นไปได้ใน C ++ และหมายถึงเกือบเหมือนกับPerson Bob = Person();
60561

3
@ user60561 ไม่มันประกาศฟังก์ชั่นโดยไม่มีข้อโต้แย้งและส่งกลับบุคคล
นิโคไล

คำตอบ:


52

จะมีบางสิ่งที่ Bob = คนใหม่ ()?

ใช่เพราะมรดก ถ้า:

public class StackExchangeMember : Person {}

แล้ว:

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

บ๊อบก็เป็นคนเช่นกันและด้วยความเขลาเขาไม่ต้องการได้รับการปฏิบัติที่แตกต่างจากคนอื่น

นอกจากนี้เราสามารถมอบบ๊อบด้วยพลังพิเศษได้:

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

IModerator bob = new StackOverFlowModerator();

ดังนั้นโดย golly เขาจะไม่ยืนรับการปฏิบัติที่แตกต่างจากผู้ดูแลคนอื่น ๆ และเขาชอบแอบไปรอบ ๆ ฟอรัมเพื่อให้ทุกคนอยู่ในบรรทัดขณะที่ไม่ระบุตัวตน:

StackExchangeMember bob = new StackOverFlowModerator();

จากนั้นเมื่อเขาพบโปสเตอร์แรกที่ไม่ดีเขาก็โยนเสื้อคลุมล่องหนของเขาออกมา

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

จากนั้นเขาก็สามารถแสดงความไร้เดียงสาและทุกสิ่งได้ในภายหลัง:

((Person) bob).ImNotMeanIWasJustInstantiatedThatWay();

20
นี่จะชัดเจนมากขึ้นถ้าคุณลดชื่อวัตถุของคุณให้ต่ำลง
การแข่งขัน Lightness กับโมนิก้า

38
ตัวอย่างที่ดีของข้อกำหนด ตัวอย่างที่ดีของการสืบทอด สำหรับผู้อื่นที่อ่านข้อความนี้โปรดอย่าพยายามแก้บทบาทผู้ใช้โดยใช้การสืบทอด
Aaronaught

8
@Aaronaught ถูกต้อง อย่าสร้างชั้นเรียนแยกต่างหากสำหรับคนประเภทต่างๆ ใช้ enumbitfield
โคลจอห์นสัน

1
@Aaraught ทุกอย่างดีบอกว่าจะไม่ทำ แต่มันไม่ได้มีประโยชน์มากถ้าไม่บอกว่าคนควรทำอะไรแทน
Pharap

5
@Pharap: ฉันได้ทำตรงนั้นในหลาย อื่น ๆ คำถาม คำตอบง่ายๆคือผู้ใช้ (การพิสูจน์ตัวตน / ข้อมูลประจำตัว) และนโยบายความปลอดภัย (การอนุญาต / การอนุญาต) ควรได้รับการปฏิบัติแยกต่างหากและแบบจำลองมาตรฐานสำหรับนโยบายความปลอดภัยนั้นอิงตามบทบาทหรือตามการอ้างสิทธิ์ การสืบทอดมีประโยชน์มากขึ้นในการอธิบายวัตถุที่ใช้ในการพิสูจน์ตัวตนเช่นการใช้ LDAP และการใช้ SQL
Aaronaught

34

ลองนำโค้ดบรรทัดแรกของคุณมาตรวจสอบดู

Person Bob = new Person();

ที่แรกPersonก็คือสเปคประเภท ใน C # เราสามารถจัดการกับสิ่งนี้โดยเพียงแค่พูดว่า

var Bob = new Person();

และเรียบเรียงจะอนุมานPerson()ประเภทของตัวแปรบ๊อบจากการเรียกตัวสร้าง

แต่คุณอาจต้องการเขียนสิ่งนี้:

IPerson Bob = new Person();

ที่คุณไม่ปฏิบัติตามสัญญา API ทั้งหมดของบุคคล IPersonแต่สัญญาที่ระบุโดยอินเตอร์เฟซ


2
+1: ฉันจะทำIPersonตัวอย่างในรหัสของฉันเพื่อให้แน่ใจว่าฉันจะไม่ใช้วิธีการส่วนตัวใด ๆ โดยไม่ได้ตั้งใจเมื่อฉันเขียนรหัสที่ควรจะคัดลอก / วางได้เพื่อIPersonการใช้งานอื่น
Cort Ammon - Reinstate Monica

@CortAmmon ฉันคิดว่าคุณมีการพิมพ์ผิดที่นั่นชัดเจนว่าคุณหมายถึง "ทำงานกับ polymorphism" แทนที่จะคัดลอก / วางบนรหัสนั้น: D
Benjamin Gruenbaum

@BenjaminGruenbaum แน่นอนสำหรับคำจำกัดความของ polymorphism ;-)
Cort Ammon - Reinstate Monica

@CortAmmon คุณจะเรียกวิธีการส่วนตัวโดยบังเอิญได้อย่างไร แน่นอนคุณหมายถึงinternalอะไร
โคลจอห์นสัน

@ColeJohnson อย่างใดอย่างหนึ่ง ฉันเขียนความคิดของกรณีเฉพาะที่ฉันวิ่งข้ามซึ่งprivateมีความหมาย: วิธีการของโรงงานที่เป็นส่วนหนึ่งของคลาสที่ถูกสร้างอินสแตนซ์ ในสถานการณ์ของฉันการเข้าถึงค่าส่วนตัวควรเป็นข้อยกเว้นไม่ใช่บรรทัดฐาน ฉันทำงานกับโค้ดที่จะอยู่ได้นานกว่าฉัน ถ้าฉันทำแบบนี้ไม่เพียง แต่ฉันไม่ได้ใช้วิธีส่วนตัวใด ๆ เท่านั้น แต่เมื่อนักพัฒนาซอฟต์แวร์คนต่อไปคัดลอก / วางสถานที่นี้สักสองสามโหลและนักพัฒนาหลังจากที่คัดลอกพวกเขามันจะลดโอกาสที่บางคนเห็นโอกาส ใช้วิธีการส่วนตัวเป็นพฤติกรรม "ปกติ"
Cort Ammon - Reinstate Monica

21
  1. ไวยากรณ์นี้ค่อนข้างเก่าจาก C ++ ซึ่งทั้งสองมี:

    Person Bob;

    และ

    Person *bob = new Bob();

    คนแรกที่สร้างวัตถุภายในขอบเขตปัจจุบันที่สองเพื่อสร้างตัวชี้ไปยังวัตถุแบบไดนามิก

  2. คุณสามารถมีได้แน่นอน something_else Bob = new Person()

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

    คุณกำลังทำสองสิ่งที่แตกต่างกันที่นี่ระบุประเภทของตัวแปรท้องถิ่นnumsและคุณบอกว่าคุณต้องการสร้างวัตถุใหม่ของ 'รายการ' ประเภทและวางไว้ที่นั่น

  3. ชนิดC #เห็นด้วยกับคุณเพราะส่วนใหญ่เวลาชนิดของตัวแปรจะเหมือนกันกับสิ่งที่คุณใส่มันดังนั้น:

    var nums = new List<int>();
  4. ในบางภาษาคุณทำอย่างดีที่สุดเพื่อหลีกเลี่ยงการระบุประเภทของตัวแปรเช่นในF # :

    let list123 = [ 1; 2; 3 ]

5
อาจจะแม่นยำมากกว่าที่จะกล่าวว่าตัวอย่างที่สองของคุณสร้างตัวชี้ไปยังวัตถุ Bob ใหม่ วิธีการจัดเก็บสิ่งต่าง ๆ นั้นเป็นรายละเอียดในการนำไปปฏิบัติ
Robert Harvey

4
"คนแรกที่สร้างวัตถุภายในเครื่องบนกองซ้อนที่สองเพื่อสร้างวัตถุบนกอง" โอ้มนุษย์ไม่ใช่ข้อมูลที่ผิดอีกครั้ง
การแข่งขัน Lightness กับโมนิก้า

@LightnessRacesinOrbit ดีขึ้นหรือไม่
AK_

1
@AK_ ในขณะที่ "ไดนามิก" หมายถึง "กอง" (เมื่อกองเป็นรูปแบบหน่วยความจำที่ใช้โดยแพลตฟอร์ม) "อัตโนมัติ" แตกต่างจาก "กองซ้อน" มาก ถ้าคุณทำเช่นnew std::pair<int, char>()นั้นสมาชิกfirstและsecondของคู่จะมีระยะเวลาการเก็บข้อมูลอัตโนมัติ แต่พวกเขาอาจถูกจัดสรรในฮีป (ในฐานะสมาชิกของpairออบเจ็กต์ไดนามิกระยะเวลาการเก็บข้อมูล)
Reinstate Monica

1
@AK_: ใช่ชื่อเหล่านั้นบ่งบอกได้อย่างแม่นยำสิ่งที่พวกเขาหมายถึงในขณะนี้ "กอง" VS "กอง" เรื่องไร้สาระไม่ได้ นั่นคือจุดทั้งหมด การที่คุณพบว่าพวกเขาสับสนเพียง แต่ตอกย้ำความต้องการที่เราจะสอนพวกเขาเพื่อที่คุณจะไม่พึ่งพาคำศัพท์ที่ไม่ถูกต้อง / ไม่ถูกต้องเพียงเพราะมันคุ้นเคย!
การแข่งขัน Lightness กับโมนิก้า

3

มีความแตกต่างกันมากระหว่างเป็นและint x เป็นเป็นและมันก็จะต้องเป็นและไม่เคยได้อะไรอื่นนอกจาก แม้ว่าคุณจะไม่เริ่มต้นเมื่อคุณประกาศมัน ( ) มันยังคงตั้งค่าเป็นค่าเริ่มต้นPerson bobintintintintintintint x;int

อย่างไรก็ตามเมื่อคุณประกาศPerson bobมีความยืดหยุ่นอย่างมากกับสิ่งที่ชื่อbobอาจอ้างถึงในเวลาใดก็ตาม มันสามารถอ้างถึงPersonหรือมันอาจหมายถึงชั้นอื่น ๆ เช่นProgrammerมาจากPerson; มันอาจnullหมายถึงไม่มีวัตถุใด ๆ เลย

ตัวอย่างเช่น:

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

นักออกแบบภาษาสามารถสร้างไวยากรณ์ทางเลือกที่จะทำให้สิ่งเดียวกันเป็นPerson carol = new Person()สัญลักษณ์ที่น้อยลง แต่พวกเขายังคงต้องยอมให้Person carol = new Person() (หรือสร้างกฎแปลก ๆ ที่ทำให้หนึ่งในสี่ตัวอย่างข้างต้นผิดกฎหมาย) พวกเขากังวลกับการรักษาภาษา "ง่าย" มากกว่าในการเขียนรหัสที่รัดกุมมาก นั่นอาจมีอิทธิพลต่อการตัดสินใจของพวกเขาที่จะไม่ให้ทางเลือกทางเลือกที่สั้นกว่า แต่ในกรณีใด ๆ มันไม่จำเป็นและพวกเขาไม่ได้ให้มัน


1

การประกาศสองครั้งอาจแตกต่างกัน แต่มักจะเหมือนกัน รูปแบบทั่วไปที่แนะนำใน Java ดูเหมือนว่า:

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

ตัวแปรเหล่านี้listและmapถูกประกาศโดยใช้อินเตอร์เฟสListและMapในขณะที่รหัสจะทำให้การใช้งานที่เฉพาะเจาะจงเป็นอินสแตนซ์ ด้วยวิธีนี้ส่วนที่เหลือของรหัสขึ้นอยู่กับอินเทอร์เฟซและง่ายต่อการเลือกคลาสการใช้งานที่แตกต่างกันเพื่ออินสแตนซ์TreeMapเนื่องจากส่วนที่เหลือของรหัสไม่สามารถขึ้นอยู่กับส่วนใดส่วนหนึ่งของHashMapAPI ที่อยู่นอกMapอินเทอร์เฟซ

อีกตัวอย่างหนึ่งที่ทั้งสองประเภทแตกต่างกันในวิธีการของโรงงานที่เลือกคลาสย่อยเฉพาะเพื่อสร้างอินสแตนซ์จากนั้นส่งคืนเป็นประเภทฐานเพื่อให้ผู้โทรไม่จำเป็นต้องทราบรายละเอียดการใช้งานเช่นตัวเลือก "นโยบาย"

การอนุมานชนิดสามารถแก้ไขความซ้ำซ้อนของซอร์สโค้ดได้ เช่นใน Java

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

จะสร้างรายการที่เหมาะสมโดยใช้การอนุมานและการประกาศ

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

ในบางภาษาการอนุมานประเภทจะดำเนินต่อไปเช่นใน C ++

auto p = new Person();

BTW ภาษา Java ชุดการประชุมที่แข็งแกร่งสำหรับการใช้ที่ต่ำกว่าชื่อกรณีที่ระบุเช่นไม่bob Bobสิ่งนี้หลีกเลี่ยงความคลุมเครือมากมายเช่นแพ็คเกจคลาสเทียบกับคลาสแปรผัน
Jerry101

... clazzและนั่นเป็นเหตุผลที่คุณมี
CVN

ไม่clazzใช้เนื่องจากclassเป็นคำหลักจึงไม่สามารถใช้เป็นตัวระบุได้
Jerry101

... ซึ่งจะไม่เป็นปัญหาหากการตั้งชื่อการประชุมไม่เป็นเช่นนั้น Classเป็นตัวระบุที่ถูกต้องสมบูรณ์
cHao

... เช่นเดียวกับcLaSs, และcLASS cLASs
el.pescado

1

ในคำพูดของคนธรรมดา:

  • การแยกการประกาศจาก instantiation ช่วย decouple ผู้ใช้วัตถุจากผู้ที่สร้างพวกเขา
  • เมื่อคุณทำเช่นนั้น polyporphism จะถูกเปิดใช้งานตราบใดที่ชนิดอินสแตนซ์เป็นประเภทย่อยของประเภทการประกาศรหัสทั้งหมดที่ใช้ตัวแปรจะทำงานได้
  • ในภาษาที่พิมพ์อย่างรุนแรงคุณจะต้องประกาศตัวแปรที่ระบุชนิดของมันโดยทำเพียงแค่var = new Process()คุณไม่ได้ประกาศตัวแปรก่อน

0

นอกจากนี้ยังเกี่ยวกับระดับการควบคุมสิ่งที่เกิดขึ้น หากการประกาศของวัตถุ / ตัวแปรเรียกตัวสร้างโดยอัตโนมัติเช่นถ้า

Person somePerson;

เป็นอัตโนมัติเช่นเดียวกับ

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

จากนั้นคุณจะไม่สามารถใช้ (ตัวอย่าง) วิธีคงที่จากโรงงานเพื่อสร้างอินสแตนซ์ของวัตถุแทนตัวสร้างเริ่มต้นนั่นคือมีบางครั้งที่คุณไม่ต้องการเรียกตัวสร้างสำหรับอินสแตนซ์วัตถุใหม่

ตัวอย่างนี้มีการอธิบายในJava ที่มีประสิทธิภาพของJoshua Bloch (รายการ 1 แดกดันพอ!)


เกี่ยวกับหนังสือทั้งหนังสือ C # และหนังสือ Java มาจาก Joyce Farrell นั่นคือสิ่งที่หลักสูตรระบุไว้ ฉันยังได้เสริมทั้งกับวิดีโอ youtube ต่างๆใน C # และ Java
Jason Wohlgemuth

ผมไม่ได้วิจารณ์เพียงแค่ให้มีการอ้างอิงถึงที่นี้มาจาก :)
เดวิด Scholefield
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.