เมื่อใดที่จะไม่เรียก super () วิธีการเมื่อแทนที่?


113

เมื่อฉันสร้างคลาสที่กำหนดเองของ Android ฉันจะextendเป็นคลาสดั้งเดิม จากนั้นเมื่อฉันต้องการที่จะแทนที่วิธีฐานที่ฉันมักจะเรียกsuper()วิธีการเช่นเดียวกับที่ฉันมักจะทำในonCreate, onStopฯลฯ

และฉันคิดว่านี่เป็นเช่นนั้นตั้งแต่แรกเริ่มทีม Android แนะนำให้เราเรียกร้องให้superลบล้างทุกวิธี

แต่ในหนังสือหลายเล่มฉันเห็นว่านักพัฒนาที่มีประสบการณ์มากกว่าตัวเองมักจะละเว้นการโทรsuperและฉันสงสัยจริงๆว่าพวกเขาทำไปเพราะขาดความรู้ ยกตัวอย่างเช่นดูที่นี้พื้นฐานแซ็กโซโฟนระดับ parser ที่superถูกละไว้ในstartElement, charactersและendElement:

public class SAXParser extends DefaultHandler{
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        if(qName.equalsIgnoreCase("XXY")) {
            //do something
        }
    }

    public void characters(char[] ch, int start, int length) throws SAXException {
        //do something
    }

    public void endElement(String uri, String localName, String qName) throws SAXException {
        if(qName.equalsIgnoreCase("XXY")) {
            //do something
        }else () {
            //do something
        }
    }
}

หากคุณพยายามสร้างเมธอดลบล้างผ่าน Eclipse หรือ IDE อื่น ๆsuperจะถูกสร้างขึ้นเป็นส่วนหนึ่งของกระบวนการอัตโนมัติเสมอ

นี่เป็นเพียงตัวอย่างง่ายๆ หนังสือเต็มรูปแบบของรหัสที่คล้ายกัน

พวกเขารู้ได้อย่างไรว่าคุณต้องโทรsuperและเมื่อใดที่คุณไม่สามารถโทรได้

ปล. อย่าผูกมัดกับตัวอย่างเฉพาะนี้ มันเป็นเพียงตัวอย่างที่สุ่มเลือกมาจากหลาย ๆ ตัวอย่าง

(นี่อาจฟังดูเป็นคำถามสำหรับมือใหม่ แต่ฉันสับสนจริงๆ)


3
endElementเอกสาร API ของ API ระบุว่า "โดยค่าเริ่มต้นไม่ต้องทำอะไรเลยผู้เขียนแอปพลิเคชันอาจแทนที่วิธีนี้ ... " ซึ่งหมายความว่าคุณสามารถโทรหา super ได้อย่างปลอดภัยเพราะมัน "ไม่มีอะไร" แต่คุณไม่จำเป็นต้องทำและคุณสามารถลบล้างได้จริงๆ คุณมักจะบอกได้ว่าคุณต้อง / สามารถ / ไม่ควรทำหรือไม่หากคุณอ่านเอกสารสำหรับวิธีการนั้น
zapl

1
ตัวอย่างเล็ก ๆ : ฉันใช้ onBackPressed () ในหน้าจอเริ่มต้นและฉันไม่เรียกซุปเปอร์ดังนั้นมันจึงไม่กระเด็นออกไปเพียงแค่ปิดใช้งานปุ่ม
Bojan Kogoj

@sandalone ขออภัยที่ถามมา ณ ที่นี้ แต่คุณมีประสบการณ์เกี่ยวกับวิธีจัดการภาษี VAT ด้วยการเรียกเก็บเงินในแอปหรือไม่และ Google จะดำเนินการให้ประเทศใดโดยอัตโนมัติและฉันต้องรายงาน / จ่ายภาษี VAT ด้วยตนเองให้กับประเทศใดบ้าง ดูstackoverflow.com/questions/36506835/…
Vidar Vestnes

คำตอบ:


144

เมื่อเรียกsuperเมธอดนี้คุณจะไม่ได้ลบล้างพฤติกรรมของเมธอดคุณกำลังขยายมัน

การโทรsuperจะดำเนินการตามตรรกะที่คลาสที่คุณกำลังขยายกำหนดไว้สำหรับวิธีการนั้น โปรดคำนึงว่ามันอาจมีความสำคัญในขณะที่คุณเรียกsuperใช้การใช้งานในวิธีการของคุณแทนที่ ตัวอย่างเช่น:

public class A { 
    public void save() { 
         // Perform save logic
    }
}

public class B extends A {
    private Object b;
    @Override
    public void save() { 
        super.save(); // Performs the save logic for A
        save(b); // Perform additional save logic
    }
}

การเรียกที่B.save()จะดำเนินการsave()ตรรกะสำหรับทั้งสองAและBตามลำดับเฉพาะนี้ หากคุณไม่ได้โทรsuper.save()ภายในB.save(), A.save()จะไม่ถูกเรียกว่า และถ้าคุณเรียกว่าsuper.save()หลังsave(b), จะดำเนินการได้อย่างมีประสิทธิภาพนั้นA.save()B.save()

หากคุณต้องการที่จะแทนที่ superพฤติกรรม (นั่นคืออย่างเต็มที่ละเว้นการดำเนินงานและให้มันเองทั้งหมด), superคุณไม่ควรจะเรียก

ในSAXParserตัวอย่างเช่นคุณให้การใช้งานของDefaultHandlerสำหรับวิธีการเหล่านั้นเป็นเพียงความว่างเปล่าเพื่อให้ subclasses สามารถแทนที่พวกเขาและให้พฤติกรรมสำหรับวิธีการเหล่านั้น ในjavadocสำหรับวิธีนี้ยังชี้ให้เห็น

public void startElement (String uri, String localName,
    String qName, Attributes attributes) throws SAXException {
    // no op
}

เกี่ยวกับการsuper()โทรเริ่มต้นในรหัสที่สร้างโดย IDE ตามที่@barsjuระบุไว้ในความคิดเห็นของเขาในตัวสร้างแต่ละตัวจะมีการเรียกโดยนัยถึงsuper()(แม้ว่าคุณจะไม่ได้เขียนไว้ในรหัสของคุณก็ตาม) ซึ่งหมายความว่าในบริบทนั้นการเรียกไปที่super' ตัวสร้างเริ่มต้น IDEเพียงแค่เขียนมันลงสำหรับคุณ แต่ก็ยังจะได้รับการเรียกถ้าคุณถอดมันออก นอกจากนี้โปรดสังเกตว่าเมื่อใช้ตัวสร้างsuper()หรือตัวแปรใด ๆ ที่มีอาร์กิวเมนต์ (เช่นsuper(x,y,z)) สามารถเรียกใช้ที่จุดเริ่มต้นของวิธีการเท่านั้น


14
นอกจากนี้โปรดทราบว่าสำหรับตัวสร้างsuper()(ตัวสร้างเริ่มต้นของคลาสระดับสูง) จะถูกเรียกเสมอแม้ว่าคุณจะไม่ได้ระบุก็ตาม หากซุปเปอร์คลาสไม่มีคอนสตรัคเตอร์เริ่มต้นคุณจะได้รับข้อผิดพลาดในการคอมไพล์หากคุณไม่เรียกหนึ่งในคอนสตรัคเตอร์ของซูเปอร์คลาสอย่างชัดเจนว่าเป็นคำสั่งแรกของคุณในตัวสร้างคลาสย่อย
Barsju

1
ใช่มันเป็นเพียงความเกียจคร้าน - สำหรับวิธีการที่เรารู้ว่าไม่ได้ทำอะไรเลยเพียงแค่ละเว้นมันเพื่อความกะทัดรัด
Voo

1
ขอบคุณสำหรับความคิดเห็นอันมีค่าของคุณ @barjsu เราได้รวมรายละเอียดเหล่านี้ไว้ในคำตอบแล้ว
Xavi López

16

พวกเขารู้ได้อย่างไรว่าคุณต้องโทรหาซูเปอร์และเมื่อใดที่คุณสามารถละเว้นการโทรได้?

โดยปกติถ้าเป็นวิธีการที่ API พิเศษมีความหมายสำคัญต่อวงจรชีวิตกรอบบริบทพื้นฐานก็จะได้รับการระบุไว้อย่างชัดเจนและเน้นในเอกสาร API เช่นเอกสารActivity.onCreate() API ยิ่งไปกว่านั้นหาก API เป็นไปตามการออกแบบที่มีประสิทธิภาพควรมีข้อยกเว้นบางประการเพื่อแจ้งเตือนนักพัฒนาผู้บริโภคในช่วงเวลารวบรวมโครงการและตรวจสอบให้แน่ใจว่าจะไม่เกิดข้อผิดพลาดในขณะดำเนินการ

หากสิ่งนี้ไม่ได้ระบุไว้อย่างชัดเจนในเอกสารประกอบ API ก็ค่อนข้างปลอดภัยสำหรับนักพัฒนาผู้บริโภคที่จะถือว่าเมธอด API ไม่จำเป็นต้องเรียกเมื่อทำการลบล้าง ขึ้นอยู่กับผู้พัฒนาผู้บริโภคที่จะตัดสินใจว่าจะใช้พฤติกรรมเริ่มต้น (เรียกใช้superเมธอด) หรือลบล้างโดยสมบูรณ์

หากเงื่อนไขได้รับอนุญาต (ฉันชอบซอฟต์แวร์โอเพนซอร์ส) นักพัฒนาผู้บริโภคสามารถตรวจสอบซอร์สโค้ด API ได้ตลอดเวลาและดูว่าวิธีการนั้นเขียนอย่างไรภายใต้ประทุน ตรวจสอบActivity.onCreate()แหล่งที่มาและDefaultHandler.startElement()แหล่งที่มาเช่น


ขอบคุณที่อธิบายสิ่งต่างๆเพิ่มเติมให้ฉัน ขอบคุณที่เตือนให้ฉันตรวจสอบซอร์สโค้ด
รองเท้าแตะ

7

การทดสอบที่คุณควรทำในหัวคือ:

"ฉันต้องการฟังก์ชั่นทั้งหมดของวิธีนี้ให้ฉันแล้วค่อยทำอะไรในภายหลัง" ถ้าใช่คุณต้องการโทรหาsuper()จากนั้นทำตามวิธีการของคุณให้เสร็จสิ้น สิ่งนี้จะเป็นจริงสำหรับวิธีการ "สำคัญ" เช่นonDraw()ซึ่งจัดการกับสิ่งต่างๆมากมายในเบื้องหลัง

ถ้าคุณต้องการบางส่วนของฟังก์ชั่น (เช่นเดียวกับวิธีการมากที่สุดที่คุณจะแทนที่) super()แล้วคุณอาจไม่ต้องการที่จะเรียกร้อง


3

ดีXaviให้คำตอบที่ดีกว่า .. แต่คุณอาจอาจจะรู้ว่าสิ่งที่ทำsuper()ทำอย่างไรเมื่อเรียกวิธีการแทนที่ ... โฆษณาว่าสิ่งที่คุณทำกับพฤติกรรมเริ่มต้น ..

เช่น:

onDraw() 

เมธอดในคลาสมุมมองเมื่อถูกแทนที่ .. คุณวาดบางอย่างก่อนที่จะพูด super.onDraw () มันจะปรากฏขึ้นเมื่อมุมมองถูกวาดจนสุดแล้ว .. ดังนั้นการโทรที่นี่จึงsuperเป็นสิ่งจำเป็นเนื่องจาก Android มีสิ่งที่สำคัญอย่างยิ่งที่ต้องทำ (เช่น onCreate ())

แต่ในขณะเดียวกัน

onLongClick()

เมื่อคุณแทนที่สิ่งนี้คุณไม่ต้องการเรียก super เพราะจะแสดงกล่องโต้ตอบพร้อมรายการตัวเลือกสำหรับ EditText หรือมุมมองอื่น ๆ ที่คล้ายกัน .. นั่นคือความแตกต่างพื้นฐาน .. คุณมีทางเลือกที่จะปล่อยไว้บางครั้ง .. แต่สำหรับ วิธีอื่น ๆ เช่นonCreate() , onStop()คุณควรปล่อยให้ OS จัดการ ..


2

ฉันไม่ได้รับคำถามของคุณอย่างชัดเจน แต่ถ้าคุณถามว่าทำไมไม่โทรหาsuperเมธอด:

มีเหตุผลในการเรียกใช้superเมธอด: หากไม่มีตัวสร้างอาร์กิวเมนต์เป็นศูนย์ในคลาสพาเรนต์จะไม่สามารถสร้างคลาสลูกสำหรับสิ่งนั้นได้ดังนั้นคุณต้องเก็บตัวสร้างอาร์กิวเมนต์ไว้ในคลาสแม่หรือคุณต้องการ เพื่อกำหนดsuper()คำสั่งเรียกโดยargument(how much argument constructor you have used in super class)ที่ด้านบนสุดของตัวสร้างคลาสลูก

ฉันหวังว่านี่จะช่วยได้. ถ้าไม่แจ้งให้เราทราบ


2

ฉันใช้รายการอาร์เรย์ข้อ จำกัด เช่น

public class ConstraintArrayList<T> extends ArrayList<T> {
  ConstraintArrayList(Constraint<T> cons) {this.cons = cons;}
  @Override
  public boolean add(T element) {
    if (cons.accept(element))
      return super.add(element);
    return false;
  }
}

หากคุณดูโค้ดก็เพียงแค่ทำการตรวจสอบล่วงหน้าก่อนที่จะปล่อยให้ซูเปอร์คลาสดำเนินการเพิ่มองค์ประกอบจริงในรายการ สิ่งนี้บอกเหตุผลหนึ่งในสองเท่าสำหรับการลบล้างวิธีการ:

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

2

สำหรับผู้ที่ยังสงสัยว่าวิธีการแทนที่จาก Android กรอบควรโทรsuperและพบว่าคำถามนี้ - นี่คือคำใบ้จากปัจจุบัน 2,019 - Android สตูดิโอ 3+ จะบอกคุณเมื่อคุณต้องการมัน

ใส่คำอธิบายภาพที่นี่

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