Tại sao Java không cho phép ghi đè các phương thức tĩnh?


534

Tại sao không thể ghi đè các phương thức tĩnh?

Nếu có thể, xin vui lòng sử dụng một ví dụ.


3
Hầu hết các ngôn ngữ OOP không cho phép điều này.
jmucchiello

7
@jmucchiello: xem câu trả lời của tôi. Tôi cũng có suy nghĩ giống như bạn, nhưng sau đó đã học về các phương pháp 'lớp' của Ruby / Smalltalk và do đó, có những ngôn ngữ OOP thực sự khác thực hiện điều này.
Kevin Brock

5
@jmucchiello hầu hết ngôn ngữ OOP không phải là ngôn ngữ OOP thực sự (tôi nghĩ về Smalltalk)
mathk


1
có thể là do Java giải quyết các cuộc gọi đến các phương thức tĩnh tại thời gian biên dịch. Vì vậy, ngay cả khi bạn đã viết Parent p = new Child()và sau đó p.childOverriddenStaticMethod()trình biên dịch sẽ giải quyết nó Parent.childOverriddenStaticMethod()bằng cách nhìn vào loại tham chiếu.
Manoj

Câu trả lời:


494

Ghi đè phụ thuộc vào việc có một thể hiện của một lớp. Điểm đa hình là bạn có thể phân lớp một lớp và các đối tượng thực hiện các lớp con đó sẽ có các hành vi khác nhau cho cùng các phương thức được định nghĩa trong lớp cha (và ghi đè trong các lớp con). Một phương thức tĩnh không được liên kết với bất kỳ trường hợp nào của một lớp nên khái niệm này không được áp dụng.

Có hai cân nhắc thúc đẩy thiết kế của Java đã tác động đến điều này. Một vấn đề liên quan đến hiệu năng: đã có rất nhiều chỉ trích về Smalltalk về việc nó quá chậm (bộ sưu tập rác và các cuộc gọi đa hình là một phần của điều đó) và những người tạo ra Java đã quyết tâm tránh điều đó. Một quyết định khác là đối tượng mục tiêu cho Java là các nhà phát triển C ++. Làm cho các phương thức tĩnh hoạt động theo cách chúng có lợi ích quen thuộc với các lập trình viên C ++ và cũng rất nhanh, vì không cần phải đợi đến khi chạy để tìm ra phương thức nào để gọi.


18
... nhưng chỉ "chính xác" trong Java. Chẳng hạn, Scala tương đương với "các lớp tĩnh" (được gọi là objects) cho phép nạp chồng các phương thức.

32
Objective-C cũng cho phép ghi đè các phương thức lớp.
Richard

11
Có một hệ thống phân cấp loại thời gian biên dịch và hệ thống phân cấp loại thời gian chạy. Thật hợp lý khi hỏi tại sao một cuộc gọi phương thức tĩnh không tận dụng được hệ thống phân cấp kiểu thời gian chạy trong những trường hợp có một. Trong Java, điều này xảy ra khi gọi một phương thức tĩnh từ một đối tượng ( obj.staticMethod()) - được phép và sử dụng các kiểu thời gian biên dịch. Khi cuộc gọi tĩnh nằm trong một phương thức không tĩnh của một lớp, đối tượng "hiện tại" có thể là một kiểu dẫn xuất của lớp - nhưng các phương thức tĩnh được định nghĩa trên các kiểu dẫn xuất không được xem xét (chúng thuộc kiểu thời gian chạy hệ thống cấp bậc).
Steve Powell

18
Tôi nên nói rõ rằng: không đúng khi khái niệm này không được áp dụng .
Steve Powell

13
Câu trả lời này, trong khi chính xác, gần giống với "nó như thế nào" hơn là nó phải như thế nào hoặc chính xác hơn là nó có thể như thế nào để đáp ứng sự mong đợi của OP và do đó, ở đây, bản thân tôi và những người khác. Không có lý do cụ thể nào để không cho phép ghi đè các phương thức tĩnh ngoài "đó là như thế nào". Tôi nghĩ đó là một lỗ hổng cá nhân.
RichieHH

186

Cá nhân tôi nghĩ rằng đây là một lỗ hổng trong thiết kế của Java. Vâng, vâng, tôi hiểu rằng các phương thức không tĩnh được gắn vào một thể hiện trong khi các phương thức tĩnh được gắn vào một lớp, v.v. Tuy nhiên, hãy xem xét đoạn mã sau:

public class RegularEmployee {
    private BigDecimal salary;

    public void setSalary(BigDecimal salary) {
        this.salary = salary;
    }

    public static BigDecimal getBonusMultiplier() {
        return new BigDecimal(".02");
    }

    public BigDecimal calculateBonus() {
        return salary.multiply(getBonusMultiplier());
    }

    /* ... presumably lots of other code ... */
}

public class SpecialEmployee extends RegularEmployee {
    public static BigDecimal getBonusMultiplier() {
        return new BigDecimal(".03");
    }
}

Mã này sẽ không hoạt động như bạn mong đợi. Cụ thể, SpecialEmployee nhận được tiền thưởng 2% giống như nhân viên bình thường. Nhưng nếu bạn loại bỏ các "tĩnh", thì SpecialEmployee sẽ nhận được 3% tiền thưởng.

. mã không liên quan đến điểm.)

Nó có vẻ khá hợp lý với tôi rằng bạn có thể muốn làm cho getBonusMultiplier tĩnh. Có lẽ bạn muốn có thể hiển thị hệ số nhân thưởng cho tất cả các loại nhân viên, mà không cần phải có một phiên bản của một nhân viên trong mỗi danh mục. Điều gì sẽ là điểm tìm kiếm cho các ví dụ như vậy? Điều gì xảy ra nếu chúng ta tạo ra một loại nhân viên mới và chưa có nhân viên nào được chỉ định? Đây là khá logic một chức năng tĩnh.

Nhưng nó không hoạt động.

Và vâng, vâng, tôi có thể nghĩ ra bất kỳ cách nào để viết lại đoạn mã trên để làm cho nó hoạt động. Quan điểm của tôi không phải là nó tạo ra một vấn đề không thể giải quyết được, mà là nó tạo ra một cái bẫy cho lập trình viên bất đắc dĩ, bởi vì ngôn ngữ không hành xử như tôi nghĩ một người hợp lý sẽ mong đợi.

Có lẽ nếu tôi đã cố gắng viết một trình biên dịch cho một ngôn ngữ OOP, tôi sẽ nhanh chóng thấy tại sao việc thực hiện nó để các hàm tĩnh có thể bị ghi đè sẽ khó khăn hoặc không thể.

Hoặc có lẽ có một số lý do chính đáng tại sao Java hành xử theo cách này. Bất cứ ai cũng có thể chỉ ra một lợi thế cho hành vi này, một số loại vấn đề được làm cho dễ dàng hơn bằng cách này? Ý tôi là, đừng chỉ cho tôi thông số ngôn ngữ Java và nói "xem, đây là tài liệu về cách nó hoạt động". Tôi biết điều đó. Nhưng có một lý do chính đáng tại sao nó NÊN hành xử theo cách này? (Bên cạnh việc rõ ràng "làm cho nó hoạt động đúng là quá khó" ...)

Cập nhật

@VicKirk: Nếu bạn muốn nói rằng đây là "thiết kế tồi" bởi vì nó không phù hợp với cách Java xử lý các số liệu thống kê, thì câu trả lời của tôi là "Chà, duh, tất nhiên." Như tôi đã nói trong bài viết gốc của mình, nó không hoạt động. Nhưng nếu bạn muốn nói rằng đó là thiết kế tồi theo nghĩa là sẽ có một cái gì đó sai về cơ bản với ngôn ngữ mà ngôn ngữ này hoạt động, tức là nơi mà các thống kê có thể bị ghi đè giống như các chức năng ảo, thì điều này bằng cách nào đó sẽ đưa ra một sự mơ hồ hoặc không thể thực hiện hiệu quả hoặc một số như vậy, tôi trả lời, "Tại sao? Có gì sai với khái niệm này?"

Tôi nghĩ rằng ví dụ tôi đưa ra là một điều rất tự nhiên muốn làm. Tôi có một lớp có một hàm không phụ thuộc vào bất kỳ dữ liệu cá thể nào và tôi rất muốn gọi độc lập với một cá thể, cũng như muốn gọi từ trong một phương thức cá thể. Tại sao điều này không hoạt động? Tôi đã gặp phải tình huống này một số lần trong những năm qua. Trong thực tế, tôi khắc phục nó bằng cách làm cho hàm ảo, và sau đó tạo một phương thức tĩnh với mục đích duy nhất trong cuộc sống là một phương thức tĩnh chuyển cuộc gọi sang phương thức ảo với một thể hiện giả. Đó dường như là một cách rất vòng để đến đó.


11
@Bemrose: Nhưng đó là quan điểm của tôi: Tại sao tôi không được phép làm điều đó? Có thể khái niệm trực quan của tôi về những gì "tĩnh" nên làm khác với bạn, nhưng về cơ bản tôi nghĩ về tĩnh là một phương thức có thể tĩnh vì nó không sử dụng bất kỳ dữ liệu cá thể nào và NÊN tĩnh vì bạn có thể muốn tĩnh để gọi nó một cách độc lập Một tĩnh rõ ràng được gắn với một lớp: Tôi hy vọng Integer.valueOf sẽ được gắn với Integers và Double.valueOf được gắn với Đôi.
Jay

9
@ewernli & Bemrose: Vâng, đúng vậy. Tôi không tranh luận về điều đó. Vì mã trong ví dụ của tôi không hoạt động, tất nhiên tôi sẽ không thử viết nó. Câu hỏi của tôi là tại sao nó là như vậy. (Tôi sợ điều này sẽ biến thành một trong những cuộc trò chuyện mà chúng tôi không liên lạc. "Xin lỗi, ông Salesman, tôi có thể lấy một trong những thứ này màu đỏ không?" "Không, nó có giá 5 đô la." "Vâng, tôi biết nó có giá 5 đô la, nhưng tôi có thể có được một màu đỏ không? "" Thưa ông, tôi chỉ nói với bạn rằng nó có giá 5 đô la. "" Được rồi, tôi biết giá, nhưng tôi đã hỏi về màu sắc. "" Tôi đã nói với bạn về giá cả! " v.v.)
Jay

6
Tôi nghĩ rằng cuối cùng mã này là khó hiểu. Xem xét nếu thể hiện được thông qua như là một tham số. Sau đó, bạn đang nói rằng cá thể thời gian chạy nên ra lệnh phương thức tĩnh nào được gọi. Điều đó về cơ bản tạo ra một hệ thống phân cấp hoàn toàn song song với thể hiện hiện tại. Bây giờ nếu một lớp con định nghĩa chữ ký phương thức giống như không tĩnh thì sao? Tôi nghĩ rằng các quy tắc sẽ làm cho mọi thứ khá phức tạp. Đó chính xác là những loại phức tạp ngôn ngữ mà Java cố gắng tránh.
Yishai

6
@Yishai: RE "Thời gian chạy lệnh cho biết phương thức tĩnh nào được gọi là": Chính xác. Tôi không thấy lý do tại sao bạn không thể làm bất cứ điều gì với một tĩnh mà bạn có thể làm với ảo. "Phân cấp riêng biệt": Tôi muốn nói, hãy biến nó thành một phần của cùng một hệ thống phân cấp. Tại sao không thống kê được bao gồm trong cùng một hệ thống phân cấp? "Subsclass định nghĩa cùng một chữ ký không tĩnh": Tôi cho rằng điều đó là bất hợp pháp, giống như nó là bất hợp pháp, giả sử, có một lớp con ghi đè một hàm với một chữ ký giống hệt nhưng một kiểu trả về khác hoặc không ném ra tất cả các ngoại lệ cha mẹ ném, hoặc để có phạm vi hẹp hơn.
Jay

28
Tôi nghĩ Jay có một điểm - điều đó cũng làm tôi ngạc nhiên khi tôi phát hiện ra rằng các số liệu thống kê không thể bị ghi đè. Một phần vì nếu tôi có A với phương thức someStatic()và B mở rộng A, sau đó B.someMethod() liên kết với phương thức trong A. Nếu sau đó tôi thêm someStatic()vào B, mã gọi vẫn gọi A.someStatic()cho đến khi tôi biên dịch lại mã gọi. Ngoài ra, điều đó làm tôi ngạc nhiên khi bInstance.someStatic()sử dụng loại bInstance được khai báo , không phải kiểu thời gian chạy vì nó liên kết tại biên dịch không liên kết, vì vậy A bInstance; ... bInstance.someStatic()gọi A.someStatic () nếu B.someStatic () tồn tại.
Lawrence Dol

42

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 đó methodlà một phương thức không tĩnh và ThisClass.method()nếu methodlà 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, privateprotectednhư vòng loại trên staticphươ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 staticpublic finalphươ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.


"Nếu chúng ta làm điều đó sẽ làm cho các phần thân phương thức khó đọc hơn: các cuộc gọi tĩnh trong lớp cha có khả năng có thể được" định tuyến lại "một cách linh hoạt." Đúng, nhưng đây chính xác là những gì xảy ra với các cuộc gọi hàm không tĩnh thông thường bây giờ. Điều này thường được quảng cáo là một tính năng tích cực cho các chức năng ảo thông thường, không phải là một vấn đề.
Jay

25

Thật ra chúng tôi đã sai.
Mặc dù Java không cho phép bạn ghi đè các phương thức tĩnh theo mặc định, nếu bạn xem xét kỹ tài liệu về các lớp Class và Phương thức trong Java, bạn vẫn có thể tìm cách mô phỏng các phương thức tĩnh được ghi đè bằng cách giải quyết sau:

import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;

class RegularEmployee {

    private BigDecimal salary = BigDecimal.ONE;

    public void setSalary(BigDecimal salary) {
        this.salary = salary;
    }
    public static BigDecimal getBonusMultiplier() {
        return new BigDecimal(".02");
    }
    public BigDecimal calculateBonus() {
        return salary.multiply(this.getBonusMultiplier());
    }
    public BigDecimal calculateOverridenBonus() {
        try {
            // System.out.println(this.getClass().getDeclaredMethod(
            // "getBonusMultiplier").toString());
            try {
                return salary.multiply((BigDecimal) this.getClass()
                    .getDeclaredMethod("getBonusMultiplier").invoke(this));
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }
        return null;
    }
    // ... presumably lots of other code ...
}

final class SpecialEmployee extends RegularEmployee {

    public static BigDecimal getBonusMultiplier() {
        return new BigDecimal(".03");
    }
}

public class StaticTestCoolMain {

    static public void main(String[] args) {
        RegularEmployee Alan = new RegularEmployee();
        System.out.println(Alan.calculateBonus());
        System.out.println(Alan.calculateOverridenBonus());
        SpecialEmployee Bob = new SpecialEmployee();
        System.out.println(Bob.calculateBonus());
        System.out.println(Bob.calculateOverridenBonus());
    }
}

Kết quả đầu ra:

0.02
0.02
0.02
0.03

những gì chúng tôi đã cố gắng để đạt được :)

Ngay cả khi chúng ta khai báo biến thứ ba Carl là RoutEmployee và gán cho thể hiện của SpecialEmployee, chúng ta vẫn sẽ có cuộc gọi của phương thức RoutEmployee trong trường hợp đầu tiên và gọi phương thức SpecialEmployee trong trường hợp thứ hai

RegularEmployee Carl = new SpecialEmployee();

System.out.println(Carl.calculateBonus());
System.out.println(Carl.calculateOverridenBonus());

Chỉ cần nhìn vào bảng điều khiển đầu ra:

0.02
0.03

;)


9
Có phản xạ là khá nhiều điều duy nhất người ta có thể làm - nhưng câu hỏi không chính xác là thế này - thật hữu ích khi có nó ở đây
Mr_and_Mrs_D

1
Câu trả lời này là vụ hack lớn nhất tôi từng thấy cho đến nay trên tất cả các chủ đề Java. Thật vui khi đọc nó vẫn còn :)
Andrejs

19

Các phương thức tĩnh được JVM coi là toàn cục, không bị ràng buộc với một thể hiện đối tượng nào cả.

Về mặt khái niệm có thể khả thi nếu bạn có thể gọi các phương thức tĩnh từ các đối tượng lớp (như trong các ngôn ngữ như Smalltalk) nhưng đó không phải là trường hợp trong Java.

BIÊN TẬP

Bạn có thể quá tải phương thức tĩnh, không sao cả. Nhưng bạn không thể ghi đè một phương thức tĩnh, vì lớp không phải là đối tượng hạng nhất. Bạn có thể sử dụng sự phản chiếu để có được lớp của một đối tượng trong thời gian chạy, nhưng đối tượng mà bạn nhận được không song song với hệ thống phân cấp lớp.

class MyClass { ... }
class MySubClass extends MyClass { ... }

MyClass obj1 = new MyClass();
MySubClass obj2 = new MySubClass();

ob2 instanceof MyClass --> true

Class clazz1 = obj1.getClass();
Class clazz2 = obj2.getClass();

clazz2 instanceof clazz1 --> false

Bạn có thể phản ánh qua các lớp, nhưng nó dừng lại ở đó. Bạn không gọi một phương thức tĩnh bằng cách sử dụng clazz1.staticMethod(), nhưng sử dụng MyClass.staticMethod(). Một phương thức tĩnh không bị ràng buộc với một đối tượng và do đó không có khái niệm nào thiscũng như supertrong một phương thức tĩnh. Một phương thức tĩnh là một hàm toàn cục; kết quả là cũng không có khái niệm về đa hình và do đó, ghi đè phương thức không có ý nghĩa.

Nhưng điều này có thể xảy ra nếu MyClasslà một đối tượng trong thời gian chạy mà bạn gọi một phương thức, như trong Smalltalk (hoặc có thể là JRuby như một bình luận gợi ý, nhưng tôi không biết gì về JRuby).

Ôi tuyệt, lại thêm một thứ nữa. Bạn có thể gọi một phương thức tĩnh thông qua một đối tượng obj1.staticMethod()nhưng đó thực sự là đường cú pháp cho MyClass.staticMethod()và nên tránh. Nó thường đưa ra một cảnh báo trong IDE hiện đại. Tôi không biết tại sao họ lại cho phép lối tắt này.


5
Ngay cả nhiều ngôn ngữ hiện đại như Ruby cũng có các phương thức lớp và cho phép ghi đè chúng.
Chandra Sekar

3
Các lớp thực sự tồn tại như các đối tượng trong Java. Xem lớp "Lớp". Tôi có thể nói myObject.getClass () và nó sẽ trả về cho tôi một thể hiện của đối tượng lớp thích hợp.
Jay

5
Bạn chỉ nhận được một "mô tả" của lớp - không phải chính lớp đó. Nhưng sự khác biệt là tinh tế.
ewernli

Bạn vẫn có lớp nhưng nó được ẩn trong VM (gần trình nạp lớp), người dùng gần như không có quyền truy cập vào nó.
toán

Để sử dụng clazz2 instanceof clazz1đúng cách, bạn có thể sử dụng class2.isAssignableFrom(clazz1), mà tôi tin rằng nó sẽ trở lại đúng trong ví dụ của bạn.
Simon Forsberg

14

Việc ghi đè phương thức được thực hiện bằng cách gửi động , nghĩa là kiểu khai báo của một đối tượng không xác định hành vi của nó, mà là kiểu thời gian chạy của nó:

Animal lassie = new Dog();
lassie.speak(); // outputs "woof!"
Animal kermit = new Frog();
kermit.speak(); // outputs "ribbit!"

Mặc dù cả hai lassiekermitđược khai báo là các đối tượng kiểu Animal, hành vi (phương thức .speak()) của chúng khác nhau bởi vì việc gửi động sẽ chỉ ràng buộc lệnh gọi phương thức .speak()với một triển khai trong thời gian chạy - không phải trong thời gian biên dịch.

Bây giờ, đây là nơi statictừ khóa bắt đầu có ý nghĩa: từ "tĩnh" là một từ trái nghĩa với "động". Vì vậy, lý do tại sao bạn không thể ghi đè các phương thức tĩnh là vì không có sự điều động động đối với các thành viên tĩnh - bởi vì nghĩa đen là "không động". Nếu chúng được gửi một cách linh hoạt (và do đó có thể bị ghi đè), statictừ khóa sẽ không còn ý nghĩa nữa.


11

Đúng. Thực tế Java cho phép ghi đè phương thức tĩnh và về mặt lý thuyết, nếu bạn ghi đè một phương thức tĩnh trong Java thì nó sẽ biên dịch và chạy trơn tru nhưng nó sẽ mất tính đa hình vốn là thuộc tính cơ bản của Java. Bạn sẽ đọc ở mọi nơi mà không thể tự mình biên dịch và chạy. bạn sẽ nhận được câu trả lời của bạn. ví dụ: Nếu bạn có Class Animal và một phương thức tĩnh eat () và bạn Ghi đè phương thức tĩnh đó trong Lớp con của nó, hãy gọi nó là Dog. Sau đó, khi bất cứ nơi nào bạn Gán một đối tượng Chó cho Tham chiếu động vật và gọi eat () theo Java Dog 'eat () nên được gọi nhưng trong trạng thái ghi đè động vật tĩnh (eat) sẽ được gọi.

class Animal {
    public static void eat() {
        System.out.println("Animal Eating");
    }
}

class Dog extends Animal{
    public static void eat() {
        System.out.println("Dog Eating");
    }
}

class Test {
    public static void main(String args[]) {
       Animal obj= new Dog();//Dog object in animal
       obj.eat(); //should call dog's eat but it didn't
    }
}


Output Animal Eating

Theo Nguyên lý đa hình của Java, đầu ra phải là Dog Eating.
Nhưng kết quả là khác nhau vì để hỗ trợ Đa hình, Java sử dụng Binding muộn, điều đó có nghĩa là các phương thức chỉ được gọi vào thời gian chạy chứ không phải trong trường hợp phương thức tĩnh. Trong trình biên dịch phương thức tĩnh gọi các phương thức tại thời gian biên dịch thay vì thời gian chạy, vì vậy chúng ta có các phương thức theo tham chiếu và không theo đối tượng tham chiếu có chứa lý do tại sao Bạn có thể nói Thực tế nó hỗ trợ ghi đè tĩnh nhưng về mặt lý thuyết, nó không 't.


3
Đó là thực tế xấu để gọi các phương thức tĩnh từ đối tượng.
Dmitry Zagorulkin

6

ghi đè được dành riêng cho các thành viên thể hiện để hỗ trợ hành vi đa hình. các thành viên lớp tĩnh không thuộc về một thể hiện cụ thể. thay vào đó, các thành viên tĩnh thuộc về lớp và kết quả là ghi đè không được hỗ trợ vì các lớp con chỉ kế thừa các thành viên thể hiện công khai và được bảo vệ và không phải là thành viên tĩnh. Bạn có thể muốn xác định một nhà máy nghiên cứu và nhà máy nghiên cứu và / hoặc các mẫu thiết kế chiến lược để đánh giá một phương pháp thay thế.


1
Bạn đã không đọc bất kỳ câu trả lời nào khác đã bao gồm điều này và làm rõ rằng đây không phải là lý do đủ để giảm giá trị ghi đè ở cấp độ khái niệm. Chúng tôi biết nó không hoạt động. Nó hoàn toàn "sạch sẽ khi muốn ghi đè các phương thức tĩnh và thực sự nó có thể thực hiện được bằng nhiều ngôn ngữ khác.
RichieHH

Richard, hãy giả sử trong một phút rằng khi tôi trả lời câu hỏi này 4 năm trước, hầu hết các câu trả lời này đã không được đăng, vì vậy đừng là một ass! Không cần phải khẳng định rằng tôi đã không đọc kỹ. Hơn nữa, bạn đã không đọc rằng chúng tôi chỉ thảo luận về việc ghi đè đối với Java. Ai quan tâm những gì có thể trong các ngôn ngữ khác. Nó không liên quan. Đi troll ở nơi khác. Nhận xét của bạn không thêm bất cứ điều gì có giá trị cho chủ đề này.
Athens Holloway

6

Trong Java (và nhiều ngôn ngữ OOP, nhưng tôi không thể nói cho tất cả; và một số ngôn ngữ hoàn toàn không có tĩnh) tất cả các phương thức đều có chữ ký cố định - các tham số và loại. Trong một phương thức ảo, tham số đầu tiên được ngụ ý: tham chiếu đến chính đối tượng và khi được gọi từ bên trong đối tượng, trình biên dịch sẽ tự động thêm vào this.

Không có sự khác biệt cho các phương thức tĩnh - chúng vẫn có chữ ký cố định. Tuy nhiên, bằng cách khai báo phương thức tĩnh, bạn đã tuyên bố rõ ràng rằng trình biên dịch không được bao gồm tham số đối tượng ngụ ý ở đầu chữ ký đó. Do đó, bất kỳ mã nào khác gọi điều này không được cố gắng đặt tham chiếu đến một đối tượng trên ngăn xếp . Nếu nó đã làm điều đó, thì việc thực thi phương thức sẽ không hoạt động vì các tham số sẽ ở sai vị trí - được dịch chuyển bởi một - trên ngăn xếp.

Vì sự khác biệt này giữa hai người; các phương thức ảo luôn có một tham chiếu đến đối tượng bối cảnh (nghĩa là this) vì vậy có thể tham chiếu bất cứ thứ gì trong heap thuộc về thể hiện đó của đối tượng. Nhưng với các phương thức tĩnh, vì không có tham chiếu nào được truyền, nên phương thức đó không thể truy cập bất kỳ biến và phương thức đối tượng nào vì bối cảnh không được biết.

Nếu bạn muốn Java sẽ thay đổi định nghĩa sao cho bối cảnh đối tượng được truyền vào cho mọi phương thức, tĩnh hoặc ảo, thì về bản chất bạn sẽ chỉ có các phương thức ảo.

Như ai đó đã hỏi trong một bình luận cho op - lý do và mục đích của bạn khi muốn tính năng này là gì?

Tôi không biết Ruby nhiều, vì điều này đã được OP đề cập, tôi đã thực hiện một số nghiên cứu. Tôi thấy rằng trong các lớp Ruby thực sự là một loại đối tượng đặc biệt và người ta có thể tạo ra (thậm chí là động) các phương thức mới. Các lớp là các đối tượng lớp đầy đủ trong Ruby, chúng không có trong Java. Đây chỉ là thứ bạn sẽ phải chấp nhận khi làm việc với Java (hoặc C #). Đây không phải là ngôn ngữ động, mặc dù C # đang thêm một số dạng động. Trong thực tế, Ruby không có các phương thức "tĩnh" như tôi có thể tìm thấy - trong trường hợp đó đây là các phương thức trên đối tượng lớp singleton. Sau đó, bạn có thể ghi đè lên singleton này bằng một lớp mới và các phương thức trong đối tượng lớp trước sẽ gọi những phương thức được định nghĩa trong lớp mới (đúng không?). Vì vậy, nếu bạn đã gọi một phương thức trong ngữ cảnh của lớp gốc, nó vẫn chỉ thực hiện các thống kê gốc, nhưng gọi một phương thức trong lớp dẫn xuất, sẽ gọi các phương thức từ lớp cha hoặc lớp con. Thú vị và tôi có thể thấy một số giá trị trong đó. Nó có một mô hình suy nghĩ khác nhau.

Vì bạn đang làm việc trong Java, bạn sẽ cần điều chỉnh theo cách làm việc đó. Tại sao họ làm điều này? Chà, có lẽ để cải thiện hiệu suất tại thời điểm đó dựa trên công nghệ và sự hiểu biết đã có sẵn. Ngôn ngữ máy tính không ngừng phát triển. Quay trở lại đủ xa và không có thứ gọi là OOP. Trong tương lai, sẽ có những ý tưởng mới khác.

EDIT : Một bình luận khác. Bây giờ tôi thấy sự khác biệt và với tư cách là nhà phát triển Java / C #, tôi có thể hiểu tại sao câu trả lời bạn nhận được từ các nhà phát triển Java có thể gây nhầm lẫn nếu bạn đến từ một ngôn ngữ như Ruby. Các staticphương thức Java không giống như classcác phương thức Ruby . Các nhà phát triển Java sẽ có một thời gian khó hiểu điều này, cũng như những người làm việc chủ yếu với một ngôn ngữ như Ruby / Smalltalk. Tôi có thể thấy điều này cũng sẽ gây nhầm lẫn rất nhiều bởi thực tế là Java cũng sử dụng "phương thức lớp" như một cách khác để nói về các phương thức tĩnh nhưng thuật ngữ tương tự này được Ruby sử dụng khác nhau. Java không có các phương thức lớp kiểu Ruby (xin lỗi); Ruby không có các phương thức tĩnh kiểu Java mà thực sự chỉ là các hàm kiểu thủ tục cũ, như được tìm thấy trong C.

Nhân tiện - cảm ơn câu hỏi! Hôm nay tôi đã học được một điều mới cho tôi về các phương thức của lớp (kiểu Ruby).


1
Tất nhiên, không có lý do nào khiến Java không thể truyền đối tượng "Class" làm tham số ẩn cho các phương thức tĩnh. Nó chỉ không được thiết kế để làm điều đó.
jmucchiello

6

Chà ... câu trả lời là KHÔNG nếu bạn nghĩ từ góc độ của một phương thức overriden nên hoạt động như thế nào trong Java. Nhưng, bạn không gặp phải bất kỳ lỗi biên dịch nào nếu bạn cố gắng ghi đè một phương thức tĩnh. Điều đó có nghĩa là, nếu bạn cố gắng ghi đè, Java sẽ không ngăn bạn làm điều đó; nhưng bạn chắc chắn không nhận được hiệu ứng giống như bạn nhận được cho các phương thức không tĩnh. Ghi đè trong Java đơn giản có nghĩa là phương thức cụ thể sẽ được gọi dựa trên loại thời gian chạy của đối tượng chứ không phải kiểu thời gian biên dịch của nó (đó là trường hợp với các phương thức tĩnh được ghi đè). Được rồi ... bất kỳ dự đoán cho lý do tại sao họ cư xử kỳ lạ? Bởi vì chúng là các phương thức lớp và do đó quyền truy cập vào chúng luôn được giải quyết trong thời gian biên dịch chỉ sử dụng thông tin loại thời gian biên dịch.

Ví dụ : chúng ta hãy thử xem điều gì xảy ra nếu chúng ta thử ghi đè một phương thức tĩnh: -

class SuperClass {
// ......
public static void staticMethod() {
    System.out.println("SuperClass: inside staticMethod");
}
// ......
}

public class SubClass extends SuperClass {
// ......
// overriding the static method
public static void staticMethod() {
    System.out.println("SubClass: inside staticMethod");
}

// ......
public static void main(String[] args) {
    // ......
    SuperClass superClassWithSuperCons = new SuperClass();
    SuperClass superClassWithSubCons = new SubClass();
    SubClass subClassWithSubCons = new SubClass();

    superClassWithSuperCons.staticMethod();
    superClassWithSubCons.staticMethod();
    subClassWithSubCons.staticMethod();
    // ...
}
}

Đầu ra : -
SuperClass: inside staticMethod
SuperClass: inside staticMethod
SubClass: inside staticMethod

Lưu ý dòng thứ hai của đầu ra. Nếu staticMethod bị ghi đè thì dòng này phải giống hệt với dòng thứ ba khi chúng ta gọi 'staticMethod ()' trên một đối tượng của Runtime Type là 'SubClass' chứ không phải là 'SuperClass'. Điều này xác nhận rằng các phương thức tĩnh luôn được giải quyết chỉ bằng cách sử dụng thông tin loại thời gian biên dịch của chúng.


5

Nói chung, không có nghĩa gì khi cho phép 'ghi đè' các phương thức tĩnh vì sẽ không có cách nào tốt để xác định nên gọi phương thức nào trong thời gian chạy. Lấy ví dụ về Nhân viên, nếu chúng ta gọi RoutEmployee.getBonusMultiplier () - phương thức nào được cho là được thực thi?

Trong trường hợp của Java, người ta có thể tưởng tượng một định nghĩa ngôn ngữ trong đó có thể 'ghi đè' các phương thức tĩnh miễn là chúng được gọi thông qua một thể hiện đối tượng. Tuy nhiên, tất cả những gì sẽ làm là thực hiện lại các phương thức lớp thông thường, thêm sự dư thừa vào ngôn ngữ mà không thực sự thêm bất kỳ lợi ích nào.


2
Theo trực giác, tôi sẽ nghĩ rằng nó nên hoạt động giống như một chức năng ảo. Nếu B mở rộng A và cả A và B có các hàm ảo có tên doStuff, trình biên dịch biết rằng các phiên bản của A nên sử dụng A.doStuff và các phiên bản của B nên sử dụng B.doStuff. Tại sao nó không thể làm tương tự với các hàm tĩnh? Rốt cuộc, trình biên dịch biết lớp nào mà mỗi đối tượng là một thể hiện của.
Jay

Erm ... Jay, một phương thức tĩnh không cần phải (nói chung là không) được gọi trong một ví dụ ...
meriton

2
@meriton, nhưng sau đó nó thậm chí còn dễ dàng hơn, phải không? Nếu một phương thức tĩnh được gọi bằng tên lớp, bạn sẽ sử dụng phương thức phù hợp với lớp.
CPerkins

Nhưng sau đó, những gì được ghi đè làm cho bạn. Nếu bạn gọi A.doStuff () thì nên sử dụng phiên bản bị ghi đè trong "B extends A" hoặc phiên bản bị ghi đè trong "C extends A". Và nếu bạn có C hoặc B thì dù sao bạn cũng đang gọi các phiên bản đó ... không cần ghi đè.
PSpeed

@meriton: Đúng là một phương thức tĩnh thường không được gọi bằng một thể hiện, nhưng tôi cho rằng đó là vì một cuộc gọi như vậy không có gì hữu ích với thiết kế hiện tại của Java! Tôi đề nghị rằng một thiết kế thay thế có thể là một ý tưởng tốt hơn. BTW, theo một nghĩa rất thực, các hàm tĩnh được gọi với một thể hiện khá thường xuyên: Khi bạn gọi hàm tĩnh từ bên trong một hàm ảo. Sau đó, bạn ngầm nhận this.feft (), tức là thể hiện hiện tại.
Jay

5

Bằng cách ghi đè chúng ta có thể tạo ra một bản chất đa hình tùy thuộc vào loại đối tượng. Phương thức tĩnh không có quan hệ với đối tượng. Vì vậy, java không thể hỗ trợ ghi đè phương thức tĩnh.


5

Tôi thích và nhân đôi nhận xét của Jay ( https://stackoverflow.com/a/2223803/1517187 ).
Tôi đồng ý rằng đây là thiết kế tồi của Java.
Nhiều ngôn ngữ khác hỗ trợ ghi đè các phương thức tĩnh, như chúng ta thấy trong các bình luận trước. Tôi cảm thấy Jay cũng đã đến Java từ Delphi như tôi.
Delphi (Object Pascal) là ngôn ngữ đầu tiên triển khai OOP.
Rõ ràng là nhiều người đã có kinh nghiệm với ngôn ngữ đó vì trước đây nó là ngôn ngữ duy nhất để viết các sản phẩm GUI thương mại. Và - vâng, chúng ta có thể trong Delphi ghi đè các phương thức tĩnh. Trên thực tế, các phương thức tĩnh trong Delphi được gọi là "phương thức lớp", trong khi Delphi có khái niệm khác về "Phương thức tĩnh Delphi" là các phương thức có ràng buộc sớm. Để ghi đè các phương thức bạn phải sử dụng liên kết trễ, hãy khai báo lệnh "ảo". Vì vậy, nó rất thuận tiện và trực quan và tôi mong đợi điều này trong Java.


3

Nó sẽ làm gì tốt để ghi đè các phương thức tĩnh. Bạn không thể gọi các phương thức tĩnh thông qua một thể hiện.

MyClass.static1()
MySubClass.static1()   // If you overrode, you have to call it through MySubClass anyway.

EDIT: Dường như thông qua một sự giám sát đáng tiếc trong thiết kế ngôn ngữ, bạn có thể gọi các phương thức tĩnh thông qua một thể hiện. Nói chung không ai làm điều đó. Lỗi của tôi.


8
"Bạn không thể gọi các phương thức tĩnh thông qua một cá thể" Thực ra, một trong những điều kỳ quặc của Java là bạn CÓ THỂ gọi các phương thức tĩnh thông qua một cá thể, mặc dù đó là một ý tưởng cực kỳ tồi tệ.
Powerlord

1
Thật vậy, Java cho phép các thành viên tĩnh được truy cập thông qua các thể hiện: xem Biến tĩnh trong Java
Richard JP Le Guen

Một IDE hiện đại phù hợp sẽ tạo ra một cảnh báo khi bạn làm điều đó, vì vậy ít nhất bạn có thể bắt được nó trong khi Oracle có thể giữ cho khả năng tương thích ngược.
Gimby

1
Không có gì sai về mặt khái niệm với việc có thể gọi một phương thức tĩnh thông qua một thể hiện. Đây là ngụy biện cho lợi ích của ngụy biện. Tại sao một cái gì đó như một cá thể Date KHÔNG gọi các phương thức tĩnh của chính nó truyền dữ liệu cá thể đến hàm thông qua giao diện gọi?
RichieHH

@RichieHH Đó không phải là những gì họ đang ngụy biện. Câu hỏi là tại sao nó được phép gọi variable.staticMethod(), thay vì Class.staticMethod(), đâu variablelà một biến có kiểu khai báo Class. Tôi đồng ý nó là thiết kế ngôn ngữ xấu.

3

Ghi đè trong Java đơn giản có nghĩa là phương thức cụ thể sẽ được gọi dựa trên kiểu thời gian chạy của đối tượng chứ không phải kiểu thời gian biên dịch của nó (đó là trường hợp với các phương thức tĩnh được ghi đè). Vì các phương thức tĩnh là các phương thức lớp, chúng không phải là các phương thức cá thể nên chúng không liên quan gì đến thực tế tham chiếu nào đang trỏ đến đối tượng hoặc đối tượng nào, bởi vì bản chất của phương thức tĩnh, nó thuộc về một lớp cụ thể. Bạn có thể xác định lại nó trong lớp con nhưng lớp con đó sẽ không biết gì về các phương thức tĩnh của lớp cha bởi vì như tôi đã nói, nó chỉ dành riêng cho lớp mà nó đã được khai báo. Truy cập chúng bằng cách sử dụng các tham chiếu đối tượng chỉ là một quyền tự do bổ sung được đưa ra bởi các nhà thiết kế Java và chúng tôi chắc chắn không nên nghĩ đến việc dừng thực hành đó chỉ khi họ hạn chế thêm chi tiết và ví dụ http://faisalbhagat.blogspot.com/2014/09/method-overriding-and-method-hiding.html


3

Bằng cách ghi đè, bạn đạt được đa hình động. Khi bạn nói ghi đè các phương thức tĩnh, các từ bạn đang cố sử dụng là mâu thuẫn.

Tĩnh nói - thời gian biên dịch, ghi đè được sử dụng cho đa hình động. Cả hai đều trái ngược nhau về bản chất, và do đó không thể được sử dụng cùng nhau.

Hành vi đa hình động xuất hiện khi lập trình viên sử dụng một đối tượng và truy cập một phương thức thể hiện. JRE sẽ ánh xạ các phương thức cá thể khác nhau của các lớp khác nhau dựa trên loại đối tượng bạn đang sử dụng.

Khi bạn nói ghi đè các phương thức tĩnh, các phương thức tĩnh chúng ta sẽ truy cập bằng cách sử dụng tên lớp, sẽ được liên kết tại thời gian biên dịch, do đó không có khái niệm về các phương thức liên kết trong thời gian chạy với các phương thức tĩnh. Vì vậy, thuật ngữ "ghi đè" các phương thức tĩnh tự nó không có nghĩa gì.

Lưu ý: ngay cả khi bạn truy cập một phương thức lớp với một đối tượng, trình biên dịch java vẫn đủ thông minh để tìm ra nó và sẽ thực hiện liên kết tĩnh.


Điều này không đúng trong nhiều trường hợp trong khi chạy phương thức tĩnh trên thực tế được gọi từ một thể hiện của lớp chứa và do đó hoàn toàn khả thi để xác định trường hợp nào của hàm sẽ gọi.
RichieHH

tĩnh không có nghĩa là biên dịch thời gian, tĩnh có nghĩa là nó bị ràng buộc với lớp hơn là bất kỳ đối tượng cụ thể nào. nó có ý nghĩa nhiều hơn nếu không có ý nghĩa hơn là tạo ra một nhà máy lớp, ngoại trừ tĩnh Box.createBoxcó ý nghĩa hơn BoxFactory.createBoxvà là một mô hình không thể tránh khỏi khi cần kiểm tra lỗi xây dựng mà không ném ngoại lệ (các nhà xây dựng không thể thất bại, họ chỉ có thể giết quá trình / ném ngoại lệ), trong khi một phương thức tĩnh có thể trả về null khi thất bại hoặc thậm chí chấp nhận các cuộc gọi lại thành công / lỗi để viết một cái gì đó như hastebin.com/codajahati.java .
Dmitry

2

Trả lời câu hỏi này rất đơn giản, phương thức hoặc biến được đánh dấu là tĩnh chỉ thuộc về lớp, Vì vậy phương thức tĩnh đó không thể được kế thừa trong lớp con vì chúng chỉ thuộc về siêu lớp.


1
Xin chào G4uKu3_Gaurav. Cảm ơn vì đã quyết định đóng góp. Tuy nhiên, chúng tôi thường mong đợi câu trả lời dài hơn và chi tiết hơn điều này.
DJClayworth

@DJClayworth bạn nên theo liên kết này để có câu trả lời chi tiết geekforgeek.org/ khăn
g1ji

Cảm ơn các liên kết. Trên thực tế, tôi ở đây để giúp đỡ những người mới đến trang web, để giải thích cách trang web hoạt động cho những người không quen với nó, không phải vì tôi cần câu trả lời cho câu hỏi.
DJClayworth

1

Giải pháp dễ dàng: Sử dụng ví dụ singleton. Nó sẽ cho phép ghi đè và kế thừa.

Trong hệ thống của tôi, tôi có lớp SingletonsRegistry, trả về thể hiện cho Class đã qua. Nếu không tìm thấy, nó được tạo.

Lớp ngôn ngữ Haxe:

package rflib.common.utils;
import haxe.ds.ObjectMap;



class SingletonsRegistry
{
  public static var instances:Map<Class<Dynamic>, Dynamic>;

  static function __init__()
  {
    StaticsInitializer.addCallback(SingletonsRegistry, function()
    {
      instances = null;
    });

  } 

  public static function getInstance(cls:Class<Dynamic>, ?args:Array<Dynamic>)
  {
    if (instances == null) {
      instances = untyped new ObjectMap<Dynamic, Dynamic>();      
    }

    if (!instances.exists(cls)) 
    {
      if (args == null) args = [];
      instances.set(cls, Type.createInstance(cls, args));
    }

    return instances.get(cls);
  }


  public static function validate(inst:Dynamic, cls:Class<Dynamic>)
  {
    if (instances == null) return;

    var inst2 = instances[cls];
    if (inst2 != null && inst != inst2) throw "Can\'t create multiple instances of " + Type.getClassName(cls) + " - it's singleton!";
  }

}

Rất tuyệt, đây là lần đầu tiên tôi nghe về ngôn ngữ lập trình Haxe :)
cyc115

1
Điều này được triển khai tốt hơn nhiều trong Java như là một phương thức tĩnh trên chính lớp đó; Singleton.get(). Sổ đăng ký chỉ là nồi hơi trên đầu, và nó loại trừ GC trên các lớp.
Lawrence Dol

Bạn nói đúng, đó là một giải pháp cổ điển. Tôi không nhớ chính xác lý do tại sao tôi chọn đăng ký, có thể có một số khung suy nghĩ dẫn đến kết quả này.
Raivo Fishmeister

1

Một phương thức tĩnh, biến, khối hoặc lớp lồng nhau thuộc về toàn bộ lớp chứ không phải là một đối tượng.

Một Phương thức trong Java được sử dụng để thể hiện hành vi của Đối tượng / Lớp. Ở đây, vì phương thức là tĩnh (tức là phương thức tĩnh được sử dụng để chỉ đại diện cho hành vi của một lớp.) Thay đổi / ghi đè hành vi của toàn bộ lớp sẽ vi phạm hiện tượng một trong những trụ cột cơ bản của lập trình hướng đối tượng, tức là sự gắn kết cao . (hãy nhớ một hàm tạo là một loại phương thức đặc biệt trong Java.)

Độ gắn kết cao - Một lớp chỉ nên có một vai trò. Ví dụ: Một lớp xe hơi chỉ nên sản xuất các vật thể xe hơi chứ không phải xe đạp, xe tải, máy bay, v.v. Nhưng lớp Xe hơi có thể có một số tính năng (hành vi) chỉ thuộc về chính nó.

Do đó, trong khi thiết kế ngôn ngữ lập trình java. Các nhà thiết kế ngôn ngữ nghĩ rằng chỉ cho phép các nhà phát triển giữ một số hành vi của một lớp cho riêng mình bằng cách làm cho một phương thức tĩnh trong tự nhiên.


Đoạn mã dưới đây cố gắng ghi đè phương thức tĩnh, nhưng sẽ không gặp phải bất kỳ lỗi biên dịch nào.

public class Vehicle {
static int VIN;

public static int getVehileNumber() {
    return VIN;
}}

class Car extends Vehicle {
static int carNumber;

public static int getVehileNumber() {
    return carNumber;
}}

Điều này là do, ở đây chúng tôi không ghi đè một phương thức mà chúng tôi chỉ khai báo lại. Java cho phép khai báo lại một phương thức (tĩnh / không tĩnh).

Việc xóa từ khóa tĩnh khỏi phương thức getVehileNumber () của lớp Xe sẽ dẫn đến lỗi biên dịch, Vì, chúng tôi đang cố gắng thay đổi chức năng của phương thức tĩnh chỉ thuộc về lớp Xe.

Ngoài ra, nếu getVehileNumber () được khai báo là cuối cùng thì mã sẽ không biên dịch, vì từ khóa cuối cùng hạn chế lập trình viên khai báo lại phương thức.

public static final int getVehileNumber() {
return VIN;     }

Nhìn chung, đây là tùy thuộc vào các nhà thiết kế phần mềm cho nơi sử dụng các phương thức tĩnh. Cá nhân tôi thích sử dụng các phương thức tĩnh để thực hiện một số hành động mà không tạo bất kỳ trường hợp nào của một lớp. Thứ hai, để che giấu hành vi của một lớp với thế giới bên ngoài.


1

Đây là một lời giải thích đơn giản. Một phương thức tĩnh được liên kết với một lớp trong khi một phương thức thể hiện được liên kết với một đối tượng cụ thể. Ghi đè cho phép gọi việc thực hiện khác nhau của các phương thức được ghi đè liên quan đến đối tượng cụ thể. Vì vậy, nó là phản trực giác để ghi đè phương thức tĩnh, thậm chí không được liên kết với các đối tượng mà là chính lớp đó ở vị trí đầu tiên. Vì vậy, các phương thức tĩnh không thể bị ghi đè dựa trên đối tượng đang gọi nó, nó sẽ luôn được liên kết với lớp nơi nó được tạo.


Làm thế nào là nó trực quan để có một public abstract IBox createBox();giao diện IBox bên trong? Box có thể triển khai IBox để ghi đè creatBox và tạo kết quả đối tượng trong IBox hợp lệ, nếu không trả về null. Các nhà xây dựng không thể trả về "null" và do đó, bạn buộc phải (1) sử dụng các ngoại lệ MỌI NƠI (những gì chúng ta làm bây giờ) hoặc (2) tạo các lớp nhà máy làm những gì tôi đã nói trước đó nhưng theo cách không có ý nghĩa đối với người mới cũng như các chuyên gia của Java (mà chúng ta cũng làm bây giờ). Phương pháp tĩnh chưa thực hiện giải quyết điều này.
Dmitry

-1

Bây giờ nhìn thấy câu trả lời ở trên, mọi người đều biết rằng chúng ta không thể ghi đè các phương thức tĩnh, nhưng người ta không nên hiểu sai về khái niệm truy cập các phương thức tĩnh từ lớp con .

Chúng ta có thể truy cập các phương thức tĩnh của siêu lớp với tham chiếu lớp con nếu phương thức tĩnh này không bị ẩn bởi phương thức tĩnh mới được định nghĩa trong lớp con.

Ví dụ, xem mã dưới đây: -

public class StaticMethodsHiding {
    public static void main(String[] args) {
        SubClass.hello();
    }
}


class SuperClass {
    static void hello(){
        System.out.println("SuperClass saying Hello");
    }
}


class SubClass extends SuperClass {
    // static void hello() {
    // System.out.println("SubClass Hello");
    // }
}

Đầu ra: -

SuperClass saying Hello

Xem các tài liệu tiên tri Java và tìm kiếm những gì bạn có thể làm trong một lớp con để biết chi tiết về việc ẩn các phương thức tĩnh trong lớp phụ.

Cảm ơn


-3

Các mã sau đây cho thấy rằng nó có thể:

class OverridenStaticMeth {   

static void printValue() {   
System.out.println("Overriden Meth");   
}   

}   

public class OverrideStaticMeth extends OverridenStaticMeth {   

static void printValue() {   
System.out.println("Overriding Meth");   
}   

public static void main(String[] args) {   
OverridenStaticMeth osm = new OverrideStaticMeth();   
osm.printValue();   

System.out.println("now, from main");
printValue();

}   

} 

1
Không, nó không có; loại khai báo tĩnh osmOverridenStaticMethkhông OverrideStaticMeth.
Lawrence Dol

2
Ngoài ra, tôi sẽ cố gắng tránh sử dụng quá nhiều Meth trong khi lập trình <nụ cười lớn>;
Lawrence Dol
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.