Tại sao chúng ta không thể có phương thức tĩnh trong một lớp bên trong không tĩnh?
Nếu tôi làm cho lớp bên trong tĩnh nó hoạt động. Tại sao?
static
.")
Tại sao chúng ta không thể có phương thức tĩnh trong một lớp bên trong không tĩnh?
Nếu tôi làm cho lớp bên trong tĩnh nó hoạt động. Tại sao?
static
.")
Câu trả lời:
Bởi vì một thể hiện của một lớp bên trong được liên kết ngầm với một thể hiện của lớp bên ngoài của nó, nó không thể định nghĩa bất kỳ phương thức tĩnh nào. Do một lớp lồng tĩnh không thể tham chiếu trực tiếp đến các biến thể hiện hoặc các phương thức được định nghĩa trong lớp kèm theo của nó, nên nó chỉ có thể sử dụng chúng thông qua một tham chiếu đối tượng, nên việc khai báo các phương thức tĩnh trong một lớp lồng tĩnh là an toàn.
A.B.sync(X)
hoặc thậm chí (từ trong A) B.sync(x)
?
Không có nhiều điểm để cho phép một phương thức tĩnh trong lớp bên trong không tĩnh; Làm thế nào bạn sẽ truy cập nó? Bạn không thể truy cập (ít nhất là ban đầu) một thể hiện của lớp bên trong không tĩnh mà không thông qua một thể hiện của lớp bên ngoài. Không có cách hoàn toàn tĩnh để tạo một lớp bên trong không tĩnh.
Đối với một lớp bên ngoài Outer
, bạn có thể truy cập một phương thức tĩnh test()
như thế này:
Outer.test();
Đối với một lớp bên trong tĩnh Inner
, bạn có thể truy cập phương thức tĩnh của nó innerTest()
như sau:
Outer.Inner.innerTest();
Tuy nhiên, nếu Inner
không phải là tĩnh, bây giờ không có cách hoàn toàn tĩnh để tham chiếu phương thức innertest
. Các lớp bên trong không tĩnh được gắn với một thể hiện cụ thể của lớp bên ngoài của chúng. Một hàm khác với một hằng số, trong đó một tham chiếu đến Outer.Inner.CONSTANT
được đảm bảo không rõ ràng theo cách mà một hàm gọi Outer.Inner.staticFunction();
không phải là. Giả sử bạn có Inner.staticFunction()
cuộc gọi getState()
đó, được xác định trong Outer
. Nếu bạn cố gắng gọi hàm tĩnh đó, bây giờ bạn có một tham chiếu mơ hồ đến lớp bên trong. Đó là, trong trường hợp nào của lớp bên trong, bạn gọi hàm tĩnh? Nó quan trọng. Xem, không có cách thực sự tĩnh để tham chiếu phương thức tĩnh đó, do tham chiếu ngầm đến đối tượng bên ngoài.
Paul Bellora là chính xác rằng các nhà thiết kế ngôn ngữ có thể đã cho phép điều này. Sau đó, họ sẽ phải không cho phép bất kỳ quyền truy cập nào vào tham chiếu ngầm đến lớp bên ngoài trong các phương thức tĩnh của lớp bên trong không tĩnh. Tại thời điểm này, giá trị của lớp này là gì nếu bạn không thể tham chiếu lớp bên ngoài, ngoại trừ tĩnh? Và nếu truy cập tĩnh là tốt, thì tại sao không khai báo toàn bộ lớp tĩnh bên trong? Nếu bạn chỉ đơn giản làm cho lớp bên trong tĩnh, thì bạn không có tham chiếu ngầm đến lớp bên ngoài và bạn không còn có sự mơ hồ này nữa.
Nếu bạn thực sự cần các phương thức tĩnh trên một lớp bên trong không tĩnh, thì có lẽ bạn cần phải suy nghĩ lại về thiết kế của mình.
Outer.Inner i = new Outer().new Inner();
Ngoài ra, các lớp bên trong được phép khai báo các hằng số tĩnh theo JLS §15.28.
Outer.Inner.staticMethod()
giống như bạn có thể truy cập Outer.Inner.CONSTANT
. "Bạn không thể truy cập ... một thể hiện của lớp bên trong không tĩnh mà không thông qua một thể hiện của lớp bên ngoài." Tại sao bạn cần một ví dụ? Bạn không cần một ví dụ Outer
để gọi Outer.staticMethod()
. Tôi biết điều này thật đáng ghét nhưng quan điểm của tôi là không có ý nghĩa gì khi đóng khung câu trả lời của bạn theo cách này. IMHO các nhà thiết kế ngôn ngữ có thể cho phép nó nếu họ muốn.
Outer.Inner.CONSTANT
và Outer.Inner.staticMethod()
là một tham chiếu đến một hằng số không có cơ hội mặc nhiên tham khảo trường hợp của Outer
trong đó Inner
đã được khởi tạo. Tất cả các tài liệu tham khảo để Outer.staticMethod()
chia sẻ cùng một trạng thái chính xác. Tất cả các tài liệu tham khảo để Outer.Inner.CONSTANT
chia sẻ cùng một trạng thái chính xác. Tuy nhiên, các tham chiếu đến Outer.Inner.staticMethod()
không rõ ràng: Trạng thái "tĩnh" không thực sự tĩnh, do tham chiếu ngầm đến lớp bên ngoài trong mỗi trường hợp của Inner
. Không có một cách thực sự rõ ràng, tĩnh để truy cập nó.
Outer.this
. Tôi đồng ý với các nhà thiết kế ngôn ngữ Java rằng không có lý do nào để cho phép các phương thức tĩnh hoặc các trường tĩnh không phải là cuối cùng trong các lớp bên trong, bởi vì mọi thứ trong một lớp bên trong phải nằm trong ngữ cảnh của lớp kèm theo.
Tôi có một lý thuyết, có thể đúng hoặc không đúng.
Trước tiên, bạn nên biết một số điều về cách các lớp bên trong được triển khai trong Java. Giả sử bạn đã có lớp học này:
class Outer {
private int foo = 0;
class Inner implements Runnable {
public void run(){ foo++; }
}
public Runnable newFooIncrementer(){ return new Inner(); }
}
Khi bạn biên dịch nó, mã byte được tạo sẽ trông như thể bạn đã viết một cái gì đó như thế này:
class Outer {
private int foo = 0;
static class Inner implements Runnable {
private final Outer this$0;
public Inner(Outer outer){
this$0 = outer;
}
public void run(){ this$0.foo++; }
}
public Runnable newFooIncrementer(){ return new Inner(this); }
}
Bây giờ, nếu chúng ta đã cho phép các phương thức tĩnh trong các lớp bên trong không tĩnh, bạn có thể muốn làm một cái gì đó như thế này.
class Outer {
private int foo = 0;
class Inner {
public static void incrFoo(){ foo++; }
}
}
... có vẻ khá hợp lý, vì Inner
lớp dường như có một hóa thân mỗi Outer
trường hợp. Nhưng như chúng ta đã thấy ở trên, các lớp bên trong không tĩnh thực sự chỉ là đường cú pháp cho các lớp "bên trong" tĩnh, vì vậy ví dụ cuối cùng sẽ tương đương với:
class Outer {
private int foo = 0;
static class Inner {
private final Outer this$0;
public Inner(Outer outer){
this$0 = outer;
}
public static void incrFoo(){ this$0.foo++; }
}
}
... mà rõ ràng sẽ không hoạt động, vì this$0
là không tĩnh. Kiểu này giải thích tại sao các phương thức tĩnh không được phép (mặc dù bạn có thể đưa ra lập luận rằng bạn có thể cho phép các phương thức tĩnh miễn là chúng không tham chiếu đến đối tượng kèm theo) và tại sao bạn không thể có các trường tĩnh không phải là cuối cùng ( nó sẽ phản trực giác nếu các thể hiện của các lớp bên trong không tĩnh từ các đối tượng khác nhau được chia sẻ "trạng thái tĩnh"). Nó cũng giải thích tại sao các trường cuối cùng được cho phép (miễn là chúng không tham chiếu đến đối tượng kèm theo).
public static double sinDeg(double theta) { ... }
một lớp tiện ích toán học bên trong thì sao?
Lý do duy nhất là "không bắt buộc", vậy tại sao phải bận tâm hỗ trợ nó?
Về mặt cú pháp, không có lý do gì để cấm một lớp bên trong có các thành viên tĩnh. Mặc dù một ví dụ vềInner
hiện được liên kết với một thể hiện của Outer
, nhưng vẫn có thể sử dụng Outer.Inner.myStatic
để giới thiệu một thành viên tĩnh Inner
nếu java quyết định làm như vậy.
Nếu bạn cần chia sẻ một cái gì đó trong số tất cả các trường hợp Inner
, bạn có thể đặt chúng vàoOuter
các thành viên tĩnh. Điều này không tệ hơn việc bạn sử dụng các thành viên tĩnh trong đó Inner
, nơi Outer
vẫn có thể truy cập bất kỳ thành viên riêng Inner
nào (không cải thiện việc đóng gói).
Nếu bạn cần chia sẻ một cái gì đó trong số tất cả các trường hợp của Inner
được tạo bởi một outer
đối tượng, sẽ có ý nghĩa hơn khi đưa chúng vào Outer
lớp như các thành viên bình thường.
Tôi không đồng ý với ý kiến rằng "một lớp lồng tĩnh gần như chỉ là một lớp cấp cao nhất". Tôi nghĩ tốt hơn là thực sự coi một lớp / lớp bên trong tĩnh là một phần của lớp bên ngoài, bởi vì họ có thể truy cập các thành viên riêng của lớp bên ngoài. Và các thành viên của lớp bên ngoài cũng là "thành viên của lớp bên trong". Vì vậy, không cần phải hỗ trợ thành viên tĩnh trong lớp bên trong. Một thành viên bình thường / tĩnh trong lớp bên ngoài sẽ đủ.
Từ: https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html
Như với các phương thức và biến đối tượng, một lớp bên trong được liên kết với một thể hiện của lớp kèm theo của nó và có quyền truy cập trực tiếp vào các phương thức và trường của đối tượng đó. Ngoài ra, vì một lớp bên trong được liên kết với một thể hiện, nó không thể định nghĩa bất kỳ thành viên tĩnh nào.
Giải thích của Oracle là hời hợt và đẹp mắt. Do không có lý do kỹ thuật hoặc cú pháp để ưu tiên các thành viên tĩnh trong một lớp bên trong (nó được cho phép trong các ngôn ngữ khác như C #), động lực của các nhà thiết kế Java có thể là hương vị khái niệm và / hoặc vấn đề thuận tiện kỹ thuật.
Đây là suy đoán của tôi:
Không giống như các lớp cấp cao nhất, các lớp bên trong phụ thuộc vào thể hiện: một thể hiện của lớp bên trong được liên kết với một thể hiện của mỗi một lớp bên ngoài của nó và có quyền truy cập trực tiếp vào các thành viên của chúng. Đây là động lực chính để có chúng trong Java. Thể hiện theo một cách khác: một lớp bên trong có nghĩa là để khởi tạo trong ngữ cảnh của một thể hiện của lớp bên ngoài. Không có một thể hiện của lớp bên ngoài, một lớp bên trong sẽ không thể sử dụng được nhiều hơn các thành viên thể hiện khác của lớp bên ngoài. Hãy coi điều này là tinh thần phụ thuộc cá thể của các lớp bên trong.
Bản chất của các thành viên tĩnh (không phải là hướng đối tượng) đụng độ với phụ thuộc thể hiện tinh thần của các lớp bên trong (mà hướng đối tượng IS) bởi vì bạn có thể tham chiếu / gọi một thành viên tĩnh của lớp bên trong mà không cần một thể hiện lớp bên ngoài sử dụng tên lớp bên trong đủ điều kiện.
Các biến tĩnh đặc biệt có thể vi phạm theo một cách khác: hai trường hợp của một lớp bên trong được liên kết với các thể hiện khác nhau của lớp bên ngoài sẽ chia sẻ các biến tĩnh. Vì các biến là một thành phần của trạng thái, nên trong thực tế, hai trường hợp lớp bên trong sẽ chia sẻ trạng thái độc lập với các thể hiện của lớp bên ngoài mà chúng được liên kết. Không phải là không thể chấp nhận được rằng các biến tĩnh hoạt động theo cách này (chúng tôi chấp nhận chúng trong Java như một sự thỏa hiệp hợp lý với độ tinh khiết OOP), nhưng có thể cho rằng có một sự vi phạm sâu sắc hơn bằng cách cho phép chúng trong các lớp bên trong có các thể hiện bên ngoài được ghép nối với các thể hiện của lớp bên ngoài thiết kế bởi. Cấm các thành viên tĩnh trong các lớp bên trong ủng hộ tinh thần phụ thuộc cá thể gặt hái thêm phần thưởng cho việc ngăn chặn hành vi phạm tội OOP sâu hơn này.
Mặt khác, không có hành vi phạm tội như vậy được đòi hỏi bởi các hằng số tĩnh, không có ý nghĩa cấu thành trạng thái và vì vậy những điều này được cho phép. Tại sao không cấm các hằng số tĩnh để có sự thống nhất tối đa với tinh thần phụ thuộc thể hiện ? Có lẽ bởi vì các hằng số không cần chiếm nhiều bộ nhớ hơn mức cần thiết (nếu chúng bị buộc phải không tĩnh thì chúng được sao chép vào mọi thể hiện của lớp bên trong có khả năng gây lãng phí). Nếu không, tôi không thể tưởng tượng lý do cho ngoại lệ.
Nó có thể không phải là lý luận vững chắc, nhưng IMO nó có ý nghĩa nhất về nhận xét khó hiểu của Oracle về vấn đề này.
Câu trả lời ngắn: Mô hình tinh thần mà hầu hết các lập trình viên có về cách hoạt động của phạm vi không phải là mô hình được sử dụng bởi javac. Phù hợp với mô hình trực quan hơn sẽ đòi hỏi một sự thay đổi lớn đối với cách javac hoạt động.
Lý do chính mà các thành viên tĩnh trong các lớp bên trong mong muốn là vì sự sạch sẽ của mã - một thành viên tĩnh chỉ được sử dụng bởi một lớp bên trong phải sống bên trong nó, thay vì phải được đặt ở lớp bên ngoài. Xem xét:
class Outer {
int outID;
class Inner {
static int nextID;
int id = nextID++;
String getID() {
return outID + ":" + id;
}
}
}
Hãy xem xét những gì đang xảy ra trong getID () khi tôi sử dụng mã định danh không đủ tiêu chuẩn "outID". Phạm vi mà số nhận dạng này xuất hiện trông giống như:
Outer -> Inner -> getID()
Ở đây, một lần nữa bởi vì đây chỉ là cách javac hoạt động, mức độ "Bên ngoài" của phạm vi bao gồm cả các thành viên tĩnh và cá thể của Outer. Điều này gây nhầm lẫn bởi vì chúng ta thường được cho là nghĩ về phần tĩnh của một lớp là một cấp độ khác của phạm vi:
Outer static -> Outer instance -> instanceMethod()
\----> staticMethod()
Theo cách nghĩ này, tất nhiên staticMethod () chỉ có thể thấy các thành viên tĩnh của Outer. Nhưng nếu đó là cách javac hoạt động, thì việc tham chiếu một biến thể hiện trong một phương thức tĩnh sẽ dẫn đến lỗi "tên không thể được giải quyết". Điều thực sự xảy ra là tên được tìm thấy trong phạm vi, nhưng sau đó, một mức độ kiểm tra bổ sung bắt đầu và chỉ ra rằng tên đó đã được khai báo trong một bối cảnh thể hiện và được tham chiếu từ ngữ cảnh tĩnh.
OK, làm thế nào điều này liên quan đến các lớp bên trong? Ngây thơ, chúng tôi nghĩ rằng không có lý do gì các lớp bên trong không thể có phạm vi tĩnh, bởi vì chúng tôi đang hình dung phạm vi hoạt động như thế này:
Outer static -> Outer instance -> Inner instance -> getID()
\------ Inner static ------^
Nói cách khác, các khai báo tĩnh trong lớp bên trong và khai báo thể hiện ở lớp bên ngoài đều nằm trong phạm vi trong bối cảnh thể hiện của lớp bên trong, nhưng cả hai đều không thực sự được lồng vào nhau; cả hai thay vào đó được lồng trong phạm vi tĩnh của Bên ngoài.
Đó không chỉ là cách javac hoạt động - có một cấp phạm vi duy nhất cho cả thành viên tĩnh và cá thể và phạm vi luôn luôn nghiêm ngặt lồng nhau. Ngay cả sự kế thừa cũng được thực hiện bằng cách sao chép các khai báo vào lớp con thay vì phân nhánh và tìm kiếm phạm vi siêu lớp.
Để hỗ trợ các thành viên tĩnh của các lớp bên trong, javac sẽ phải phân chia phạm vi tĩnh và cá thể và hỗ trợ phân cấp phạm vi phân nhánh và nối lại, hoặc nó sẽ phải mở rộng ý tưởng "bối cảnh tĩnh" đơn giản của nó để thay đổi để theo dõi loại bối cảnh ở mọi cấp độ của lớp lồng nhau trong phạm vi hiện tại.
Tại sao chúng ta không thể có phương thức tĩnh trong một lớp bên trong không tĩnh?
Lưu ý: Một lớp lồng không tĩnh được gọi là lớp bên trong nên bạn không có non-static inner class
như vậy.
Một thể hiện của lớp bên trong không có sự tồn tại mà không có một thể hiện tương ứng của lớp bên ngoài. Một lớp bên trong không thể khai báo các thành viên tĩnh ngoài các hằng số thời gian biên dịch. Nếu nó được cho phép thì sẽ có sự mơ hồ về ý nghĩa của static
. Trong trường hợp đó, đã có những nhầm lẫn nhất định:
Đó là lý do tại sao các nhà thiết kế có thể đã quyết định không xử lý vấn đề này.
Nếu tôi làm cho lớp bên trong tĩnh nó hoạt động. Tại sao ?
Một lần nữa, bạn không thể tạo một lớp bên trong tĩnh thay vì bạn có thể khai báo một lớp tĩnh là lồng nhau. Trong trường hợp đó, lớp lồng này thực sự là một phần của lớp bên ngoài và có thể có các thành viên tĩnh mà không có vấn đề gì.
Chủ đề này đã thu hút được sự chú ý từ nhiều người, tôi vẫn sẽ cố gắng giải thích bằng những thuật ngữ đơn giản nhất.
Đầu tiên, với tham chiếu đến http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.1 , một lớp hoặc giao diện được khởi tạo ngay lập tức trước khi xảy ra / gọi đầu tiên của bất kỳ thành viên nào được đặt trước bởi từ khóa tĩnh.
Vì vậy, nếu chúng ta đưa ra một thành viên tĩnh trong một lớp bên trong, nó sẽ dẫn đến việc khởi tạo lớp bên trong, không nhất thiết là lớp bên ngoài / bao vây. Vì vậy, chúng tôi cản trở trình tự khởi tạo lớp.
Cũng xem xét thực tế rằng một lớp bên trong không tĩnh được liên kết với thể hiện của một lớp bao quanh / lớp ngoài. Vì vậy, liên kết với một thể hiện sẽ có nghĩa là lớp bên trong sẽ tồn tại bên trong một thể hiện của lớp ngoài và sẽ khác nhau giữa các thể hiện.
Đơn giản hóa điểm, để truy cập thành viên tĩnh, chúng ta cần một thể hiện của lớp Ngoài, từ đó chúng ta sẽ lại cần tạo một thể hiện của lớp bên trong không tĩnh. Các thành viên tĩnh không bị ràng buộc với các thể hiện và do đó bạn nhận được lỗi biên dịch.
Một lớp bên trong là một cái gì đó hoàn toàn khác với một lớp lồng tĩnh mặc dù cả hai đều giống nhau về cú pháp. Các lớp lồng nhau tĩnh chỉ là một phương tiện để nhóm trong khi các lớp bên trong có một liên kết mạnh mẽ - và truy cập vào tất cả các giá trị của - lớp bên ngoài của chúng. Bạn nên chắc chắn lý do tại sao bạn muốn sử dụng một lớp bên trong và sau đó nó sẽ trở nên khá tự nhiên mà bạn phải sử dụng. Nếu bạn cần khai báo một phương thức tĩnh thì có lẽ đó là một lớp lồng tĩnh mà bạn muốn.
giả sử có hai trường hợp của lớp bên ngoài và cả hai đều có lớp bên trong được khởi tạo. Bây giờ nếu lớp bên trong có một thành viên tĩnh thì nó sẽ chỉ giữ một bản sao của thành viên đó trong vùng heap. Trong trường hợp này cả hai đối tượng của lớp bên ngoài sẽ đề cập đến điều này một bản sao duy nhất và họ có thể thay đổi nó cùng nhau. Điều này có thể gây ra tình huống "Đọc bẩn" do đó để ngăn chặn Java này đã áp dụng hạn chế này. Điểm mạnh khác để hỗ trợ cho lập luận này là java cho phép các thành viên tĩnh cuối cùng ở đây, những người có giá trị không thể thay đổi từ một trong các đối tượng lớp bên ngoài. Xin vui lòng cho tôi nếu tôi sai.
Trước hết tại sao ai đó muốn xác định thành viên tĩnh trong một lớp bên trong không tĩnh? Câu trả lời là, để thành viên lớp bên ngoài có thể sử dụng các thành viên tĩnh đó chỉ với tên lớp bên trong, phải không?
Nhưng đối với trường hợp này, chúng ta có thể định nghĩa trực tiếp thành viên trong lớp bên ngoài. sẽ được liên kết với tất cả các đối tượng của lớp bên trong, bên trong thể hiện của lớp bên ngoài.
như mã dưới đây,
public class Outer {
class Inner {
public static void method() {
}
}
}
có thể được viết như thế này
public class Outer {
void method() {
}
class Inner {
}
}
Vì vậy, theo tôi, không làm phức tạp mã java mà nhà thiết kế không cho phép chức năng này hoặc chúng ta có thể thấy chức năng này trong các bản phát hành trong tương lai với một số tính năng khác.
Hãy cố gắng coi lớp học như một lĩnh vực bình thường, sau đó bạn sẽ hiểu.
//something must be static. Suppose something is an inner class, then it has static keyword which means it's a static class
Outer.something
Thật vô ích khi có các thành viên trong lớp là tĩnh vì bạn sẽ không thể truy cập chúng ngay từ đầu.
Hãy suy nghĩ về điều này, để truy cập một thành viên tĩnh, bạn sử dụng className.memberName ,, trong trường hợp của chúng tôi, nó phải là một cái gì đó giống như ngoài className.innerclassName.memberName ,,, bây giờ bạn có thấy tại sao innerclass phải tĩnh ....