2011-07-20 30 views
6

Về cơ bản, điều tôi muốn làm là buộc phân lớp để gọi phương thức siêu lớp trừu tượng (được thực hiện trong phân lớp), vì vậy tôi không phải viết nó một cách rõ ràng thời gian tôi tạo một phân lớp mới.Làm thế nào để bắt buộc lớp con gọi một phương thức trừu tượng được thực hiện

Tôi đã viết nó trong hàm tạo của lớp bậc cao một lần, bởi vì tôi muốn nó ép buộc nó cho mỗi lần triển khai.

public abstract class SupahClass { 
    public SupahClass() { 
     doStuff(); // It IS executed when the subclass constructor is called 
     init(); // NOT executed, even though it's implemented 
    } 

    private void doStuff() { ... }   

    protected abstract void init(); 
} 

public class SomeSubClass extends SupahClass { 

    // The problem lies HERE: this is executed AFTER init() ... so it gets NULL again 
    private TextBox myTextBox = null; 

    public SomeSubClass() { 
     super(); // invokes the super constructor, so init() should be called 
     // I could call init(); here EACH time i create a new subclass... but no :) 
    } 

    @Override 
    public void init() { 
     this.myTextBox = new TextBox(); // Executed BEFORE its declared as null above 
    } 
} 

Tất nhiên, lớp cha có thể không thực sự gọi nó là từ một bản tóm tắt phương pháp (vì vậy không xác định) của nó, nhưng một lớp trừu tượng của nó nên nó không thể được instanciated, nó phải ủy thác nhiệm vụ để lớp con của nó, vậy tại sao họ không thể gọi phương pháp trừu tượng nhưng hiện nay được thực hiện?

EDIT Xem lớp con sở hữu myTextBoxinit() thực hiện

Những phương pháp nào bạn nghĩ mình nên làm gì? Xóa = null trong tuyên bố thuộc tính (duhhh)

hoặc xóa init() trong lớp cha và gọi rõ ràng trong trình tạo lớp con (đây là những gì tôi muốn tránh vì tôi sẽ phải viết 100% thời gian) ..)

+1

Điều đó sẽ hiệu quả. Đây rõ ràng không phải là mã thực sự của bạn - mã thực của bạn phải khác với mã này theo một số cách quan trọng. Tại sao không đăng mã thực tế của bạn và để chúng tôi có một cái nhìn? –

+0

Thật vậy!xem bình luận của tôi về câu trả lời của Jon Skeet và câu hỏi được cập nhật của tôi :) – dominicbri7

Trả lời

2

Bạn có thể khắc phục điều này bằng cách thay đổi tuyên bố của bạn để:

private TextBox myTextBox; 

Việc chuyển nhượng để null phục vụ không có mục đích hữu ích. Nếu không có siêu lớp, nó sẽ không làm gì cả, vì các trường được khởi tạo là null. Vì có một siêu lớp, nó hoạt động như một tiếng súng bắn vào chân. Vì vậy, hãy loại bỏ nó.

+0

Vâng, đó chính xác là những gì tôi đã làm để khắc phục điều này, nhưng bạn đã làm cho tôi nhận ra nó không hữu ích chút nào và thực sự là một thực hành tồi. Tôi thậm chí không thể nghĩ đến một trường hợp hữu ích ... Tôi đoán tôi sẽ xóa các bài tập này ở mọi nơi tôi đã sử dụng chúng. Nhưng ngoài ra, bạn nghĩ gì về việc thực hiện hiện tại của tôi (gọi các phương thức như vậy trong các nhà xây dựng)? – dominicbri7

+0

Đó là, như mọi người sẽ cho bạn biết, một thực tế nguy hiểm. Nó mở ra cánh cửa cho một số lỗi khá phức tạp (các phương pháp chạy chống lại các đối tượng được xây dựng một phần, xuất bản đối tượng không an toàn, v.v.). Tuy nhiên, nó không phải là không thể tránh khỏi gây ra những lỗi - nó vẫn lên đến lập trình để giới thiệu chúng. Nếu không có lợi thế để gọi một phương thức lớp con từ một constructor, tôi khuyên bạn không bao giờ làm điều đó. Nhưng thông thường, đó là một cách đơn giản và rõ ràng để có được hành vi bạn muốn, không có cách thay thế đơn giản. Vì vậy, có một cuộc gọi phán quyết về việc liệu giá trị của cuộc gọi có lớn hơn nguy cơ lỗi hay không. –

+0

Đó là một cuộc gọi phán xét phụ thuộc vào ngữ cảnh - về mức độ phức tạp của các bất biến xung quanh lớp, các lập trình viên có kỹ năng làm việc trên codebase như thế nào, cho dù lớp đó sẽ được mở rộng rộng rãi hay bị giới hạn trong một phần nhỏ của hệ thống, vv Đó là một câu hỏi cụ thể, mà tôi không thể cho bạn một câu trả lời chung. Jon Skeet có thể, tất nhiên :). –

8

Tôi không thể tạo lại điều này. init()sẽ được gọi, giả sử không có trường hợp ngoại lệ nào được ném trước. Ví dụ ngắn nhưng đầy đủ:

abstract class Superclass { 
    public Superclass() { 
     init(); 
    } 

    protected abstract void init(); 
} 

class Subclass extends Superclass { 
    public Subclass() { 
     super(); 
    } 

    @Override 
    public void init() { 
     System.out.println("Subclass.init called"); 
    } 
} 

public class Test {  
    public static void main(String[] args) throws Exception { 
     new Subclass(); 
    } 
} 

Điều đó in "Subclass.init được gọi" như mong đợi. Tôi nghi ngờ cái gì khác là sai trong mã mà bạn đã không cho chúng ta thấy. Lưu ý rằng việc gọi các phương thức ảo trong một hàm khởi tạo là một công việc rủi ro - lớp con sẽ không được khởi tạo - tất cả các biến sẽ có các giá trị mặc định của chúng, ví dụ. Nó thường là một mô hình để tránh.

+1

sắp sửa gợi ý điều này nhưng không hề xấu hổ khi bị Jon Skeet đánh đập – Leon

+0

Bạn hoàn toàn đúng .. Tôi nghĩ rằng Init không bao giờ được gọi vì tại một thời điểm nào đó các thuộc tính lớp con là null khi nó được cho là được khởi tạo. Bạn đã hai lần đúng khi bạn nói đó là một công việc mạo hiểm vì lớp con sẽ không được khởi tạo: chính xác đó là vấn đề của tôi. Xem Câu hỏi đã cập nhật của tôi ở cuối để biết thêm thông tin, và tôi thực sự thích phản hồi của bạn về cách tiếp cận bạn nghĩ mình nên sử dụng .. :) – dominicbri7

2

Lớp trừu tượng có thể gọi phương thức trừu tượng.

0

Bạn đã sử dụng trình gỡ lỗi để hiểu mã đang làm gì chưa?

Thứ tự xây dựng là 1. hàm tạo của lớp cơ sở 2. khởi tạo tất cả các thành viên của lớp dẫn xuất 3. constructor của lớp dẫn xuất.

Trong trường hợp bước 1 của bạn khởi tạo thành viên (trong init của lớp dẫn xuất - được gọi thông qua cuộc gọi đa hình) nhưng bước 2 đặt nó thành rỗng.

Các vấn đề liên quan