2010-05-14 50 views
15

Tôi đang gặp khó khăn khi gói đầu quanh các lớp lồng nhau không tĩnh trong Java. Hãy xem xét ví dụ sau, in "Bên trong" và sau đó là "Trẻ em".Java: Các lớp lồng nhau không tĩnh và instance.super()

class Outer { 
    class Inner { 
     Inner() { System.out.println("Inner"); } 
    } 
} 

public class Child extends Outer.Inner { 
    Child(Outer o) { 
     o.super(); 
     System.out.println("Child"); 
    } 
    public static void main(String args[]) { 
     new Child(new Outer()); 
    } 
} 

Tôi hiểu rằng các trường hợp bên trong luôn phải được liên kết với trường hợp bên ngoài và trường hợp đó cũng áp dụng cho trẻ vì nó mở rộng bên trong. Câu hỏi của tôi là ý nghĩa của cú pháp o.super() - tại sao nó gọi hàm dựng bên trong?

Tôi chỉ thấy một đơn giản super(args) được sử dụng để gọi hàm tạo siêu lớp và super.method() để gọi phiên bản siêu lớp của một phương thức ghi đè, nhưng không bao giờ có dạng nào đó ở dạng instance.super().

+0

LOL ... điều này có thẻ 'câu hỏi phỏng vấn' là gì? – Cristian

+0

Là một phần của một cuộc phỏng vấn cho một vị trí Java, tôi đã được yêu cầu hoàn thành một bài kiểm tra IKM; câu hỏi này là một hình thức đơn giản của một câu đố. – Kiv

+0

@Kiv bạn có ví dụ về cách sử dụng thực tế trong cuộc sống không? – chepseskaf

Trả lời

9

Nó được gọi là "trình tạo hàm tạo lớp bậc trên đủ điều kiện".

Trích dẫn từ here:

Explicit tuyên bố gọi constructor có thể được chia thành hai loại:

  • lời gọi constructor thay thế bắt đầu với từ khóa này (có thể mở đầu với các đối số kiểu tường minh). Chúng được sử dụng để gọi một hàm tạo thay thế của cùng một lớp.

  • Khởi tạo hàm tạo siêu lớp bắt đầu bằng từ khóa siêu (có thể được bắt đầu bằng đối số loại rõ ràng) hoặc biểu thức Chính. Chúng được sử dụng để gọi một hàm tạo của lớp cha trực tiếp. lời gọi constructor lớp cha có thể được chia thêm:

  • không đủ điều kiện lời gọi constructor lớp cha bắt đầu với các siêu từ khóa (có thể mở đầu với các đối số kiểu tường minh).

  • Yêu cầu trình tạo hàm lớp bậc trên đủ điều kiện bắt đầu bằng biểu thức Chính. Chúng cho phép một hàm tạo lớp con xác định rõ ràng thể hiện bao bọc ngay lập tức của đối tượng mới được tạo ra đối với lớp cha trực tiếp (§8.1.3). Điều này có thể cần thiết khi lớp cha là một lớp bên trong.

9

Lớp Nội (lớp con không tĩnh) cơ bản là lớp lồng nhau (lớp trẻ tĩnh) với các liên kết tiềm ẩn trở lại đối tượng cha mẹ của họ. Đây là mã trên của bạn, được viết thay vì sử dụng lớp lồng nhau tĩnh:

class Outer { 
    static class Inner { 
     final Outer outer; 
     Inner(Outer outer) { 
      this.outer = outer; 
      System.out.println("Inner"); 
     } 
    } 
} 

public class Child extends Outer.Inner { 
    Child(Outer o) { 
     super(o); // o.super(); 
     System.out.println("Child"); 
    } 

    public static void main(String args[]) { 
     new Child(new Outer()); 
    } 
}

Nhìn vào điều này, bạn sẽ có thể hiểu o.super() đang làm gì.

+2

Trong thực tế, một lớp bên trong không tĩnh là cơ bản cú pháp đường cho ở trên, bởi sự hiểu biết của tôi. –

+0

Không, một lớp tĩnh lồng nhau về cơ bản là một lớp cấp độ bên ngoài, đó là chỉ để thuận tiện cho việc đóng gói. –

+1

Vâng, đúng vậy. Tôi chỉ có nghĩa là một lớp * bên trong không tĩnh là cú pháp đường trên đầu đường cú pháp :-D –

2

Khái niệm, một lớp bên trong không tĩnh “thuộc về” đối tượng cụ thể. Nó phân loại như mỗi người có phiên bản riêng của lớp, giống như một trường hoặc phương thức không tĩnh thuộc về một đối tượng cụ thể.

Vì vậy, đó là lý do tại sao chúng ta có cú pháp hài hước như instance.new Inner()instance.super() - cho bối cảnh nơi câu trả lời cho câu hỏi “nhưng Inner?” Không phải là ngay lập tức rõ ràng. (Trong một phương pháp không tĩnh của lớp bên ngoài, bạn chỉ cần nói new Inner(), và như thường lệ đó là viết tắt của this.new Inner().)

5

Tại sao o.super() trong Child kết thúc lên gọi Outer.Inner constructor? Thật đơn giản: bởi vì Child extends Outer.Inner và các cuộc gọi của nhà xây dựng luôn được xích lại theo thứ bậc.

Dưới đây là một mở rộng nhẹ để đoạn mã của bạn để minh họa:

class Outer { 
    Outer() { 
     System.out.println("Outer"); 
    } 
    void outerMethod() { } 
    class Inner { 
     Inner() { 
      System.out.println("OuterInner"); 
      outerMethod();    
     } 
     String wealth; 
    } 
} 
class OuterChild extends Outer { 
    OuterChild() { 
     System.out.println("OuterChild"); 
    } 
} 
public class OuterInnerChild extends Outer.Inner { 
    OuterInnerChild(Outer o) { 
     o.super(); 
     System.out.println("OuterInnerChild"); 
     this.wealth = "ONE MILLION DOLLAR!!!"; 
    } 
    public static void main(String args[]) { 
     System.out.println(new OuterInnerChild(new Outer()).wealth); 
     new OuterChild(); 
    } 
} 

in này:

Outer 
OuterInner 
OuterInnerChild 
ONE MILLION DOLLAR!!! 
Outer 
OuterChild 

Một số quan sát chính:

  • OuterInnerChild extends Outer.Inner, nó thừa hưởng wealth, giống như ngữ nghĩa phân lớp bình thường
    • Và cũng giống như ngữ nghĩa lớp con bình thường, các nhà xây dựng của OuterInnerChild dây chuyền đến các nhà xây dựng của Outer.Inner
  • OuterChild extends Outer, xích constructor của nó, ngay cả khi không gọi rõ ràng
    • Dù ngầm hay rõ ràng, các các chuỗi hàm dựng lên cấu trúc phân cấp

Nhưng tại sao trình biên dịch yêu cầu trình xây dựng OuterInnerChild mất Outer oo.super() được gọi?

Bây giờ đó là đặc trưng cho ngữ nghĩa lớp bên trong: nó được thực hiện để đảm bảo rằng tất cả các trường của OuterInnerChild đã một kèm Outer dụ cho Outer.Inner, lớp siêu của OuterInnerChild. Nếu không, hàm tạo của Outer.Inner sẽ không có phiên bản kèm theo là Outer để gọi số outerMethod() vào.

0

Luôn không quên các nguyên tắc cơ bản, trong quá trình gọi hàm khởi tạo lớp con, lớp luôn được khởi tạo trước bất kể lớp bên trong/bên ngoài. Trong kịch bản của bạn, khi bạn đang mở rộng lớp bên trong và lớp bên trong của bạn là một thành viên của lớp cha cần được khởi tạo sau đó gọi hàm dựng bên trong thực tế.

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