Khi nào KHÔNG gọi phương thức super () khi ghi đè?


113

Khi tôi tạo lớp tùy chỉnh Android của riêng mình, tôi là extendlớp gốc của nó. Sau đó, khi tôi muốn ghi đè lên các phương pháp cơ bản, tôi luôn gọi super()phương pháp, giống như tôi luôn luôn làm trong onCreate, onStopvv

Và tôi nghĩ đây là nó, vì ngay từ đầu nhóm Android đã khuyên chúng tôi nên luôn superghi đè mọi phương thức.

Tuy nhiên, trong nhiều cuốn sách, tôi có thể thấy rằng các nhà phát triển, có kinh nghiệm hơn bản thân tôi, thường bỏ qua việc gọi điện supervà tôi thực sự nghi ngờ họ làm như vậy là thiếu kiến ​​thức. Ví dụ, nhìn vào cơ bản này SAX lớp phân tích cú pháp nơi superđược bỏ qua trong startElement, charactersendElement:

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
        }
    }
}

Nếu bạn cố gắng tạo bất kỳ phương thức ghi đè nào thông qua Eclipse hoặc bất kỳ IDE nào khác, supersẽ luôn được tạo như một phần của quy trình tự động.

Đây chỉ là một ví dụ đơn giản. Sách có đầy đủ các mã tương tự .

Làm thế nào để họ biết khi nào bạn phải gọi supervà khi nào bạn có thể bỏ qua cuộc gọi?

Tái bút. Không ràng buộc với ví dụ cụ thể này. Đó chỉ là một ví dụ được chọn ngẫu nhiên từ nhiều ví dụ.

(Điều này nghe có vẻ giống như một câu hỏi dành cho người mới bắt đầu, nhưng tôi thực sự bối rối.)


3
endElementTài liệu API của nói "Theo mặc định, không làm gì cả. Người viết ứng dụng có thể ghi đè phương thức này ..." có nghĩa là bạn có thể gọi super một cách an toàn vì nó "không có gì" nhưng bạn không cần phải làm như vậy và bạn thực sự có thể ghi đè nó. Bạn thường có thể biết mình phải / có thể / không nên làm điều đó nếu bạn đọc tài liệu về phương pháp đó.
zapl

1
Ví dụ nhỏ: Tôi sử dụng onBackPressed () trong màn hình giật gân và tôi KHÔNG gọi là super nên nó không ra khỏi splash, nó chỉ tắt nút.
Bojan Kogoj

@sandalone Xin lỗi bạn đã hỏi ở đây, nhưng bạn có bất kỳ kinh nghiệm nào về cách xử lý thuế VAT với Thanh toán trong ứng dụng không và google thực hiện tự động ở những quốc gia nào và tôi phải báo cáo / nộp thuế VAT theo cách thủ công cho quốc gia nào? xem stackoverflow.com/questions/36506835/…
Vidar Vestnes

Câu trả lời:


144

Bằng cách gọi superphương thức, bạn không ghi đè hành vi của phương thức, bạn đang mở rộng nó.

Lệnh gọi tới supersẽ thực hiện bất kỳ logic nào mà lớp bạn đang mở rộng đã xác định cho phương thức đó. Hãy lưu ý rằng điều quan trọng có thể là thời điểm bạn superthực hiện lệnh gọi trong ghi đè phương thức của mình. Ví dụ:

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
    }
}

Lệnh gọi tới B.save()sẽ thực hiện save()logic cho cả hai ABtheo thứ tự cụ thể này. Nếu bạn không gọi super.save()bên trong B.save(), A.save()sẽ không được gọi. Và nếu bạn gọi super.save()sau save(b), A.save()sẽ được thực hiện hiệu quả sau đó B.save().

Nếu bạn muốn ghi đè super hành vi của (nghĩa là hoàn toàn bỏ qua việc triển khai nó và tự mình cung cấp tất cả), bạn không nên gọi super.

Trong SAXParserví dụ bạn cung cấp, các triển khai của DefaultHandlercác phương thức đó chỉ trống, do đó các lớp con có thể ghi đè chúng và cung cấp một hành vi cho các phương thức đó. Trong javadoc cho phương pháp này, điều này cũng được chỉ ra.

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

Về lệnh super()gọi mặc định trong mã được tạo bởi IDE, như @barsjuđã chỉ ra trong nhận xét của anh ấy, trong mỗi hàm tạo có một lệnh gọi ngầm định đến super()(ngay cả khi bạn không viết nó trong mã của mình), có nghĩa là, trong ngữ cảnh đó, một lệnh gọi tới super' hàm tạo mặc định. Nó IDEchỉ viết nó ra cho bạn, nhưng nó cũng sẽ được gọi nếu bạn xóa nó. Cũng lưu ý rằng khi triển khai các hàm tạo, super()hoặc bất kỳ biến thể nào của nó có đối số (tức là super(x,y,z)) chỉ có thể được gọi ở phần đầu của phương thức.


14
Cũng không lưu ý rằng đối với các hàm tạo super()(hàm tạo mặc định của lớp siêu) luôn được gọi, ngay cả khi bạn không chỉ định nó. Nếu lớp siêu không có hàm tạo mặc định, bạn sẽ gặp lỗi biên dịch nếu bạn không gọi rõ ràng một trong các hàm tạo của lớp siêu làm câu lệnh đầu tiên của bạn trong hàm tạo của lớp con.
barju

1
Vâng, nó thực chất chỉ lười biếng - cho các phương pháp mà chúng ta biết không làm gì cả chỉ bỏ qua nó cho ngắn gọn
Voo

1
Cảm ơn những nhận xét quý giá của bạn, @barjsu, tôi đã đưa những chi tiết này vào câu trả lời.
Xavi López

16

Làm thế nào để họ biết khi nào bạn phải gọi super và khi nào bạn có thể bỏ qua nó?

Thông thường, nếu một phương pháp API đặc biệt có ý nghĩa quan trọng đối với vòng đời của ngữ cảnh khung cơ bản, thì nó sẽ luôn được nêu rõ ràng và đánh dấu trong tài liệu API, như Activity.onCreate()tài liệu API . Hơn nữa, nếu API tuân theo một thiết kế mạnh mẽ, nó sẽ đưa ra một số ngoại lệ để cảnh báo cho nhà phát triển người tiêu dùng vào thời gian biên dịch dự án và đảm bảo rằng nó sẽ không tạo ra lỗi khi chạy.

Nếu điều này không được nêu rõ ràng trong tài liệu API, thì sẽ khá an toàn cho nhà phát triển tiêu dùng khi cho rằng phương thức API không bắt buộc phải gọi khi ghi đè nó. Nhà phát triển người tiêu dùng quyết định sử dụng hành vi mặc định (gọi superphương thức) hay ghi đè hoàn toàn nó.

Nếu điều kiện được cho phép (tôi yêu thích phần mềm nguồn mở), nhà phát triển người tiêu dùng luôn có thể kiểm tra mã nguồn API và xem phương pháp thực sự được viết như thế nào. Kiểm tra Activity.onCreate()nguồnDefaultHandler.startElement()nguồn chẳng hạn.


Cảm ơn đã giải thích những điều bổ sung cho tôi. Cảm ơn vì đã nhắc tôi kiểm tra mã nguồn.
sandalone

7

Bài kiểm tra bạn nên làm trong đầu là:

"Tôi có muốn tất cả các chức năng của phương pháp này được thực hiện cho tôi và sau đó làm gì đó sau đó không?" Nếu có, thì bạn muốn gọi super(), rồi kết thúc phương thức của mình. Điều này sẽ đúng với các phương thức "quan trọng" chẳng hạn như phương thức onDraw()xử lý rất nhiều thứ trong nền.

Nếu bạn chỉ muốn một số chức năng (như với hầu hết các phương thức mà bạn sẽ ghi đè) thì có thể bạn không muốn gọi super().


3

À, Xavi đã đưa ra một câu trả lời hay hơn .. nhưng bạn có thể biết những gì sẽ super()làm khi được gọi trong một phương thức bị ghi đè ... nó quảng cáo bạn đã làm gì với hành vi mặc định ..

ví dụ:

onDraw() 

phương thức trong lớp xem khi bị ghi đè .. bạn vẽ thứ gì đó trước khi nói super.onDraw () nó xuất hiện sau khi chế độ xem được vẽ hoàn toàn .. vì vậy việc gọi ở đây superlà cần thiết vì android có một số việc cực kỳ quan trọng phải làm (như onCreate ())

nhưng tại cùng một thời điểm

onLongClick()

khi bạn ghi đè cái này, bạn không muốn gọi là super vì nó sẽ xuất hiện một hộp thoại với danh sách các tùy chọn cho EditText hoặc bất kỳ chế độ xem tương tự nào khác .. Đó là sự khác biệt cơ bản .. bạn có lựa chọn để lại nó một lúc .. nhưng đối với các phương pháp khác như onCreate() , onStop()bạn nên để hệ điều hành xử lý ..


2

Tôi chưa hiểu rõ câu hỏi của bạn, nhưng nếu bạn đang hỏi về lý do tại sao không gọi superphương thức:

Có một lý do để gọi superphương thức: nếu không có phương thức khởi tạo đối số nào trong lớp cha thì không thể tạo lớp con cho phương thức đó, vì vậy bạn cần giữ một phương thức khởi tạo không đối số trong lớp cha hoặc bạn cần để định nghĩa super()câu lệnh gọi với argument(how much argument constructor you have used in super class)ở trên cùng của hàm tạo lớp con.

Tôi hi vọng cái này giúp được. Nếu không, hãy cho tôi biết.


2

Tôi đã triển khai danh sách mảng ràng buộc như

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;
  }
}

Nếu bạn nhìn vào mã, nó chỉ đơn giản là thực hiện một số kiểm tra trước trước khi thực sự cho phép siêu lớp thực hiện việc bổ sung thực tế phần tử vào danh sách. Điều này cho biết một trong hai lý do gấp cho việc ghi đè phương thức:

  1. Khả năng mở rộng nơi bạn muốn mở rộng những gì mà lớp siêu cấp có thể làm
  2. Tính cụ thể mà bạn muốn thêm hành vi cụ thể thông qua tính đa hình, chẳng hạn như trong ví dụ phổ biến của Vương quốc động vật về ngữ nghĩa di chuyển trong đó cách chim di chuyển (bay) và ếch di chuyển (nhảy) cụ thể cho từng lớp con.

2

Đối với những người còn tự hỏi phương thức ghi đè nào từ Android Framework nên gọi supervà tìm thấy câu hỏi này - đây là gợi ý hiện tại từ năm 2019 - Android Studio 3+ sẽ cho bạn biết khi nào bạn cần .

nhập mô tả hình ảnh ở đây

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.