2009-03-19 6 views
10

Trong một chương trình Java, tôi có nhiều lớp con kế thừa từ một phụ huynh (trừu tượng). Tôi muốn thể hiện rằng mỗi đứa trẻ nên có một thành viên được đặt một lần duy nhất (mà tôi đã định làm từ nhà xây dựng). Kế hoạch của tôi là viết mã s.th. như thế này:Trong Java, tại sao tôi không thể khai báo thành viên cuối cùng (w/o khởi tạo nó) trong lớp cha và đặt giá trị của nó trong lớp con? Làm thế nào tôi có thể làm việc xung quanh?

public abstract class Parent { 
    protected final String birthmark; 
} 

public class Child extends Parent { 
    public Child(String s) { 
     this.birthmark = s; 
    } 
} 

Tuy nhiên, điều này dường như không làm hài lòng các vị thần Java. Trong lớp cha, tôi nhận được thông báo rằng birthmark "có thể chưa được khởi tạo", trong lớp con tôi nhận được "Trường cuối cùng birthmark không thể truy cập được".

Vậy cách Java là gì? Tôi đang thiếu gì?

+0

Không phải là câu trả lời trực tiếp, nhưng trong các trường mẫu cuối cùng định dạng tệp lớp chỉ có thể được đặt bởi cùng một tệp lớp nhưng không nhất thiết phải trong hàm tạo. IIRC. –

+0

Câu hỏi liên quan: http://stackoverflow.com/questions/14383276/initialize-member-of-abstract-class-without-subclasses-having-write-access – user905686

Trả lời

19

Bạn không thể làm điều đó bởi vì khi so sánh các tầng lớp phụ huynh, trình biên dịch không thể chắc chắn rằng lớp con sẽ khởi tạo nó. Bạn sẽ phải khởi tạo nó trong constructor của cha mẹ, và có đứa trẻ gọi constructor của cha mẹ:

public abstract class Parent { 
    protected final String birthmark; 
    protected Parent(String s) { 
     birthmark = s; 
    } 
} 

public class Child extends Parent { 
    public Child(String s) { 
     super(s); 
     ... 
    } 
} 
+3

+1 để bảo vệ ctor cha mẹ! –

+0

Nếu tôi bỏ qua việc khai báo hàm khởi tạo của con (và, trong trường hợp đó, có thể giữ nguyên ctor của cha mẹ), tôi có thể làm 'new Child ("set this")' và lấy cái tôi muốn, đúng không? –

7

đèo nó vào constructor mẹ:

public abstract class Parent { 
    private final String birthmark; 
    public Parent(String s) { 
     birthmark = s; 
    } 
} 

public class Child extends Parent { 
    public Child(String s) { 
     super(s); 
    } 
} 
+0

Trong trường hợp của tôi, tôi không thể chuyển vì đối tượng yêu cầu tham chiếu đến 'này', bất kỳ mẹo nào? –

2

Một cách khác Java-ish để làm điều này có lẽ là để có lớp cha mẹ để xác định một "getter" trừu tượng và để các em thực hiện nó. Nó không phải là một cách tuyệt vời để làm điều đó trong trường hợp này, nhưng nó trong một số trường hợp nó có thể được chính xác những gì bạn muốn.

+0

gọi phương thức getter trừu tượng trong hàm khởi tạo có thể có nguy cơ tiềm tàng tuy nhiên, một quy tắc tốt là không bao giờ gọi một phương thức có thể ghi đè, hoặc là directlry hoặc gián tiếp, từ một hàm tạo. – TofuBeer

+0

@TofuBeer ví dụ nào có thể nguy hiểm đến mức nào? câu trả lời này sẽ làm việc hoàn hảo cho tôi [ở đây] (http://stackoverflow.com/questions/34822434/composite-inheritance-how-to-assign-a-final-field-at-sub-class-constructor-whic)! EDIT [mmm] (http://stackoverflow.com/questions/3404301/whats-wrong-with-overridable-method-calls-in-constructors) –

+0

Nếu bạn gọi một phương thức ghi đè trong một hàm tạo, và phương thức đó truy cập một biến từ lớp con thì biến đó sẽ không được khởi tạo vì hàm tạo của lớp con chưa được khởi tạo. Nó là nguy hiểm vì nó có thể làm việc ngay bây giờ, nhưng nếu ai đó thay đổi getter sau này để làm điều đó nó sẽ sụp đổ hoặc có được giá trị sai. – TofuBeer

0

Có, các thành viên cuối cùng sẽ được chỉ định trong lớp mà chúng được khai báo. Bạn cần thêm một hàm tạo với đối số String cho Parent.

0

Khai báo một hàm tạo trong lớp cha được gọi bởi lớp con. Bạn phải đặt trường trong siêu lớp để đảm bảo trường được khởi tạo hoặc trình biên dịch không thể chắc chắn trường được khởi tạo.

0

Bạn có thể muốn có một hàm tạo Cha mẹ (String birthmark) để bạn có thể đảm bảo trong lớp cha mẹ của bạn mà cuối cùng luôn được khởi tạo. Sau đó, bạn có thể gọi super (birthmark) từ Child() constructor của bạn.

1

Tại sao không ủy quyền khởi tạo cho một phương thức. Sau đó ghi đè lên phương thức trong lớp cha.

public class Parent { 
    public final Object x = getValueOfX(); 
    public Object getValueOfX() { 
     return y; 
    } 
} 
public class Child { 
    @Override 
    public Object getValueOfX() { 
    // whatever ... 
    } 
} 

Điều này sẽ cho phép khởi tạo tùy chỉnh.

+1

Thành ngữ này là một ý tưởng thực sự tồi tệ ... nếu người dùng tạo getValueofX() phụ thuộc vào bất kỳ trường nào của lớp Con, sẽ có một NullPointerException, vì Child chưa thực sự được xây dựng. Nó có thể hoạt động nếu bạn rất cẩn thận với tài liệu, nhưng người dùng ngu ngốc/độc hại có thể phá vỡ nó trong một nhịp tim. –

+0

@Chamelaeon: Đây là một nhận xét ngu ngốc như vậy, một người dùng độc hại hoặc ngu ngốc sẽ làm gì khi viết mã cho dự án của bạn? Một người dùng độc hại hoặc ngu ngốc có thể viết System.exit (0)? –

+0

điều này sẽ giúp [ở đây] (http://stackoverflow.com/questions/34822434/composite-inheritance-how-to-assign-a-final-field-at-sub-class-constructor-whic), thx! –

2

tôi sẽ làm điều đó như thế này:

public abstract class Parent 
{ 
    protected final String birthmark; 

    protected Parent(final String mark) 
    { 
     // only if this makes sense. 
     if(mark == null) 
     { 
      throw new IllegalArgumentException("mark cannot be null"); 
     } 

     birthmark = mark; 
    } 
} 

public class Child 
    extends Parent 
{ 
    public Child(final String s) 
    { 
     super(s); 
    } 
} 

thức có nghĩa là biến thể được khởi tạo một lần mỗi trường hợp. Trình biên dịch không thể đảm bảo rằng mọi lớp con sẽ cung cấp việc gán cho dấu sinh để nó buộc nhiệm vụ xảy ra trong hàm tạo của lớp cha.

Tôi đã thêm kiểm tra chỉ để chỉ ra rằng bạn cũng có được lợi ích khi có thể kiểm tra các đối số ở một nơi thay vì mỗi cosntructor.

+0

Có lý do gì để đánh dấu các tham số trong khai báo hàm cuối cùng không? Trình biên dịch cho phép tôi lấy đi mà không có. –

+0

Tôi làm điều đó ra khỏi thói quen ... dừng bạn làm param = instanceVariable. Nếu hotspot là bao giờ có thể để tối ưu hóa cuối cùng vars tốt hơn (tôi không nghĩ rằng nó hiện nay) sau đó mã của tôi sẽ kỳ diệu được nhanh hơn quá :-) – TofuBeer