Phương thức `Final` của Java: nó hứa hẹn điều gì?


141

Trong một lớp Java, một phương thức có thể được định nghĩa là final, để đánh dấu rằng phương thức này có thể không bị ghi đè:

public class Thingy {
    public Thingy() { ... }
    public int operationA() {...}
    /** this method does @return That and is final. */
    public final int getThat() { ...}
}

Điều đó rõ ràng, và nó có thể được sử dụng để bảo vệ chống lại việc ghi đè vô tình, hoặc có thể là hiệu suất - nhưng đó không phải là câu hỏi của tôi.

Câu hỏi của tôi là: Từ quan điểm OOP tôi hiểu rằng, bằng cách định nghĩa một phương thức, finalngười thiết kế lớp hứa hẹn phương thức này sẽ luôn hoạt động như được mô tả hoặc ngụ ý. Nhưng thường thì điều này có thể nằm ngoài tầm ảnh hưởng của tác giả lớp, nếu những gì phương thức đang làm phức tạp hơn thì chỉ cần phân phối một thuộc tính .

Các ràng buộc cú pháp là rõ ràng đối với tôi, nhưng ý nghĩa của ý nghĩa OOP là gì? Được finalsử dụng chính xác theo nghĩa này bởi hầu hết các tác giả lớp?

Một loại "hợp đồng" nào mà một finalphương pháp hứa hẹn?

Câu trả lời:


155

Như đã đề cập, final được sử dụng với một phương thức Java để đánh dấu rằng phương thức không thể bị ghi đè (đối với phạm vi đối tượng) hoặc ẩn (đối với tĩnh). Điều này cho phép nhà phát triển ban đầu tạo chức năng không thể thay đổi bởi các lớp con và đó là tất cả sự đảm bảo mà nó cung cấp.

Điều này có nghĩa là nếu phương thức phụ thuộc vào các thành phần tùy chỉnh khác như các trường / phương thức không công khai thì chức năng của phương thức cuối cùng vẫn có thể được tùy chỉnh. Điều này là tốt mặc dù (với đa hình) nó cho phép tùy chỉnh một phần.

Có một số lý do để ngăn thứ gì đó không thể tùy chỉnh, bao gồm:

  • Hiệu suất - Một số trình biên dịch có thể phân tích và tối ưu hóa hoạt động, đặc biệt là trình biên dịch không có tác dụng phụ.

  • Có được dữ liệu được đóng gói - nhìn vào các Đối tượng bất biến trong đó các thuộc tính của chúng được đặt tại thời điểm xây dựng và không bao giờ được thay đổi. Hoặc một giá trị tính toán có được từ các thuộc tính đó. Một ví dụ điển hình là Stringlớp Java .

  • Độ tin cậy và hợp đồng - Đối tượng được tạo thành từ nguyên thủy ( int, char, double, vv) và / hoặc các đối tượng khác. Không phải tất cả các hoạt động áp dụng cho các thành phần đó nên được áp dụng hoặc thậm chí hợp lý khi chúng được sử dụng trong Đối tượng lớn hơn. Các phương pháp với công cụ finalsửa đổi có thể được sử dụng để đảm bảo rằng. Lớp Counter là một ví dụ tốt.


public class Counter {
    private int counter = 0;

    public final int count() {
        return counter++;
    }

    public final int reset() {
        return (counter = 0);
    }
}

Nếu public final int count()phương thức này không có final, chúng ta có thể làm một cái gì đó như thế này:

Counter c = new Counter() {   
    public int count() {
        super.count();   
        return super.count();   
    } 
}

c.count(); // now count 2

Hoặc thứ gì đó giống thế này:

Counter c = new Counter() {
    public int count() {
        int lastCount = 0;
        for (int i = super.count(); --i >= 0; ) {
            lastCount = super.count();
        }

        return lastCount;
    }
}

c.count(); // Now double count

27

Một loại "hợp đồng" nào mà một phương pháp cuối cùng hứa hẹn?

Nhìn nó theo một cách khác, bất kỳ phương thức không cuối cùng nào cũng đảm bảo rằng bạn có thể ghi đè nó bằng cách thực hiện của riêng bạn và lớp sẽ vẫn hoạt động như mong đợi. Khi bạn không thể đảm bảo rằng lớp của bạn hỗ trợ ghi đè một phương thức, bạn nên làm cho nó cuối cùng.


Nhưng quan điểm này không ngụ ý cho câu hỏi ban đầu của tôi "phương pháp cuối cùng này sẽ luôn luôn hoạt động như đã hứa" mà tôi không được gọi bất kỳ phương thức không cuối cùng nào từ bên trong một phương thức cuối cùng? Bởi vì, nếu tôi thực hiện phương thức được gọi có thể đã bị ghi đè và do đó tôi không thể đảm bảo hành vi của phương thức cuối cùng của mình?
Towi 8/2/2016

8

Trước hết, bạn có thể đánh dấu các lớp không trừu tượng finalcũng như các trường và phương thức. Bằng cách này, cả lớp không thể được phân lớp. Vì vậy, hành vi của lớp sẽ được cố định.

Tôi đồng ý rằng các phương thức đánh dấu finalkhông đảm bảo rằng hành vi của chúng sẽ giống nhau trong các lớp con nếu các phương thức này đang gọi các phương thức không phải là cuối cùng. Nếu hành vi thực sự cần phải được sửa chữa, điều này phải đạt được bằng quy ước và thiết kế cẩn thận. Và đừng quên khái niệm này trong javadoc! (Tài liệu java)

Cuối cùng nhưng không kém finalphần quan trọng , từ khóa có vai trò rất quan trọng trong Mô hình bộ nhớ Java (JMM). JMM đảm bảo rằng để đạt được khả năng hiển thị của finalcác trường bạn không cần đồng bộ hóa phù hợp. Ví dụ:

class A implements Runnable {
  final String caption = "Some caption";                           

  void run() {
    // no need to synchronize here to see proper value of final field..
    System.out.println(caption);
  }
}  

vâng tôi biết về các lớp học cuối cùng - trường hợp dễ dàng. Điểm hay về các trường cuối cùng với JMM, không cần đồng bộ hóa ... hmm: điều này chỉ đề cập đến "con trỏ", phải không? Tôi vẫn có thể sửa đổi không đồng bộ đối tượng mà nó đề cập đến (ok, không phải trong String, nhưng trong các lớp do người dùng định nghĩa). Nhưng những gì bạn nói về "trận chung kết không đảm bảo hành vi" chính xác là quan điểm của tôi. Tôi đồng ý, doc và thiết kế là quan trọng.
Towi

@towi bạn đúng finalkhông đảm bảo khả năng hiển thị các thay đổi được thực hiện cho các đối tượng ghép, chẳng hạn như Map.
Victor Sorokin

0

Tôi không chắc bạn có thể đưa ra bất kỳ xác nhận nào về việc sử dụng "bản cuối cùng" và tác động của nó đến hợp đồng thiết kế chung của phần mềm. Bạn được đảm bảo rằng không có nhà phát triển nào có thể ghi đè phương thức này và làm mất hiệu lực hợp đồng theo cách đó. Nhưng mặt khác, phương pháp cuối cùng có thể dựa vào các biến lớp hoặc trường hợp có giá trị được thiết lập bởi các lớp con, và có thể gọi các phương thức lớp khác được ghi đè. Vì vậy, cuối cùng là một đảm bảo rất yếu.


1
Vâng, đó là những gì tôi có nghĩa. Đúng. Tôi thích thuật ngữ "bảo đảm yếu" :-) Và tôi (hầu hết) thích C ++ const. Như trong char const * const = "Hello"hoặc char const * const addName(char const * const name) const...
Towi

0

Không, nó không nằm ngoài tầm ảnh hưởng của tác giả lớp. Bạn không thể ghi đè nó trong lớp dẫn xuất của mình, do đó nó sẽ thực hiện những gì tác giả lớp cơ sở dự định.

http://doad.oracle.com/javase/tutorial/java/IandI/final.html

Đáng chú ý là phần mà nó gợi ý rằng các phương thức được gọi từ các hàm tạo nên là final.


1
Vâng, về mặt kỹ thuật nó sẽ làm những gì tác giả lớp cơ sở đã viết .
Joey
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.