Câu trả lời ngắn gọn là: hoàn toàn có thể, nhưng Java không làm điều đó.
Đây là một số mã minh họa trạng thái hiện tại của Java:
Tập tin Base.java
:
package sp.trial;
public class Base {
static void printValue() {
System.out.println(" Called static Base method.");
}
void nonStatPrintValue() {
System.out.println(" Called non-static Base method.");
}
void nonLocalIndirectStatMethod() {
System.out.println(" Non-static calls overridden(?) static:");
System.out.print(" ");
this.printValue();
}
}
Tập tin Child.java
:
package sp.trial;
public class Child extends Base {
static void printValue() {
System.out.println(" Called static Child method.");
}
void nonStatPrintValue() {
System.out.println(" Called non-static Child method.");
}
void localIndirectStatMethod() {
System.out.println(" Non-static calls own static:");
System.out.print(" ");
printValue();
}
public static void main(String[] args) {
System.out.println("Object: static type Base; runtime type Child:");
Base base = new Child();
base.printValue();
base.nonStatPrintValue();
System.out.println("Object: static type Child; runtime type Child:");
Child child = new Child();
child.printValue();
child.nonStatPrintValue();
System.out.println("Class: Child static call:");
Child.printValue();
System.out.println("Class: Base static call:");
Base.printValue();
System.out.println("Object: static/runtime type Child -- call static from non-static method of Child:");
child.localIndirectStatMethod();
System.out.println("Object: static/runtime type Child -- call static from non-static method of Base:");
child.nonLocalIndirectStatMethod();
}
}
Nếu bạn chạy cái này (tôi đã làm nó trên máy Mac, từ Eclipse, sử dụng Java 1.6), bạn sẽ nhận được:
Object: static type Base; runtime type Child.
Called static Base method.
Called non-static Child method.
Object: static type Child; runtime type Child.
Called static Child method.
Called non-static Child method.
Class: Child static call.
Called static Child method.
Class: Base static call.
Called static Base method.
Object: static/runtime type Child -- call static from non-static method of Child.
Non-static calls own static.
Called static Child method.
Object: static/runtime type Child -- call static from non-static method of Base.
Non-static calls overridden(?) static.
Called static Base method.
Ở đây, những trường hợp duy nhất có thể gây ngạc nhiên (và câu hỏi liên quan đến) dường như là trường hợp đầu tiên :
"Loại thời gian chạy không được sử dụng để xác định phương thức tĩnh nào được gọi, ngay cả khi được gọi với một đối tượng ( obj.staticMethod()
)."
và trường hợp cuối cùng :
"Khi gọi một phương thức tĩnh từ bên trong một phương thức đối tượng của một lớp, phương thức tĩnh được chọn là phương thức có thể truy cập được từ chính lớp đó chứ không phải từ lớp xác định loại thời gian chạy của đối tượng."
Gọi với một đối tượng
Cuộc gọi tĩnh được giải quyết tại thời gian biên dịch, trong khi cuộc gọi phương thức không tĩnh được giải quyết tại thời gian chạy. Lưu ý rằng mặc dù các phương thức tĩnh được kế thừa (từ cha mẹ) nhưng chúng không bị ghi đè (bởi con). Điều này có thể là một bất ngờ nếu bạn mong đợi khác.
Gọi từ bên trong một phương thức đối tượng
Các cuộc gọi phương thức đối tượng được giải quyết bằng cách sử dụng loại thời gian chạy, nhưng các cuộc gọi phương thức tĩnh ( lớp ) được giải quyết bằng cách sử dụng loại thời gian biên dịch (khai báo).
Thay đổi các quy tắc
Để thay đổi các quy tắc này, để cuộc gọi cuối cùng trong ví dụ được gọi Child.printValue()
, các cuộc gọi tĩnh sẽ phải được cung cấp một loại tại thời gian chạy, thay vì trình biên dịch giải quyết cuộc gọi tại thời gian biên dịch với lớp được khai báo của đối tượng (hoặc bối cảnh). Các cuộc gọi tĩnh sau đó có thể sử dụng hệ thống phân cấp loại (động) để giải quyết cuộc gọi, giống như các cuộc gọi phương thức đối tượng thực hiện ngày nay.
Điều này sẽ dễ dàng thực hiện được (nếu chúng tôi thay đổi Java: -O) và hoàn toàn không hợp lý, tuy nhiên, nó có một số cân nhắc thú vị.
Việc xem xét chính là chúng ta cần quyết định những cuộc gọi phương thức tĩnh nào sẽ thực hiện việc này.
Hiện tại, Java có "sự giải quyết" bằng ngôn ngữ, theo đó obj.staticMethod()
các cuộc gọi được thay thế bằng ObjectClass.staticMethod()
các cuộc gọi (thông thường có cảnh báo). [ Lưu ý: ObjectClass
là loại thời gian biên dịch obj
.] Đây sẽ là những ứng cử viên tốt để ghi đè theo cách này, lấy loại thời gian chạy obj
.
Nếu chúng ta đã làm điều đó sẽ làm cho các cơ quan phương pháp khó đọc: các cuộc gọi tĩnh trong một lớp cha mẹ có khả năng có thể tự động "tái định tuyến". Để tránh điều này, chúng ta sẽ phải gọi phương thức tĩnh với tên lớp - và điều này làm cho các cuộc gọi được giải quyết rõ ràng hơn với hệ thống phân cấp kiểu thời gian biên dịch (như bây giờ).
Các cách khác để gọi một phương thức tĩnh là khó khăn hơn: this.staticMethod()
nên có nghĩa tương tự như obj.staticMethod()
, lấy kiểu thời gian chạy của this
. Tuy nhiên, điều này có thể gây ra một số vấn đề đau đầu với các chương trình hiện có, gọi các phương thức tĩnh (rõ ràng là cục bộ) mà không cần trang trí (tương đương với this.method()
).
Vì vậy, những gì về cuộc gọi không được thực hiện staticMethod()
? Tôi đề nghị họ làm giống như ngày nay và sử dụng bối cảnh lớp địa phương để quyết định phải làm gì. Nếu không sự nhầm lẫn lớn sẽ xảy ra sau đó. Tất nhiên, điều đó method()
có nghĩa là this.method()
nếu đó method
là một phương thức không tĩnh và ThisClass.method()
nếu method
là một phương thức tĩnh. Đây là một nguồn gây nhầm lẫn khác.
Những ý kiến khác
Nếu chúng ta thay đổi hành vi này (và thực hiện các cuộc gọi tĩnh có khả năng tự động phi địa phương), chúng ta có lẽ sẽ muốn xem xét lại ý nghĩa của final
, private
và protected
như vòng loại trên static
phương pháp của một lớp. Sau đó chúng tôi sẽ có tất cả để làm quen với thực tế là private static
và public final
phương pháp này không ghi đè, và do đó có thể được giải quyết một cách an toàn tại thời gian biên dịch, và là "an toàn" để đọc tài liệu tham khảo như địa phương.