Tại sao Thread không phải là một lớp trừu tượng và start () không phải là cuối cùng?


79

Tại sao Threadlớp được triển khai như một lớp thông thường chứ không phải là một lớp trừu tượng với run()phương thức là trừu tượng.

Nó sẽ có thể giới thiệu bất kỳ vấn đề? Hay nó có ích gì khi theo cách này?

Ngoài ra, Thread.start()phương thức được cho là một phương thức rất cụ thể mà chức năng của nó không thể được thực hiện bởi bất kỳ lớp nào khác (Nếu tôi không nhầm). Và do đó tôi đoán finaltừ khóa sẽ phù hợp với phương pháp này hơn bất kỳ phương pháp nào khác.

Nhưng tôi có thể ghi đè phương pháp này và sử dụng nó theo ý muốn,

public class Test extends Thread {
    public static void main (String... args) {
        Thread test = new Test();
        test.start();
    }

    @Override
    public void run() {
        System.out.println("New thread started...");
    }

    @Override
    public void start() {
        System.out.println("Did anyone tell you I will spawn a new thread??");
    }
}

Nó rõ ràng chỉ được in,

Có ai nói với bạn rằng tôi sẽ sinh ra một chủ đề mới không ??

Có công dụng nào trong việc ghi đè ngoài việc khiến kỹ sư thay thế bạn bối rối không?

Nếu không, tại sao phương thức không được khai báo cuối cùng trong lớp Thread?


Ở đây bạn đang gọi phương pháp bắt đầu như một thành viên của lớp ... nó không được gọi từ lịch ...
CoderNeji

2
Chỉ để rõ ràng - bạn đang hỏi tại sao Thread.run () không trừu tượng, vì người dùng cần ghi đè nó và tại sao Thread.start () không phải là cuối cùng, vì nó không có ý nghĩa gì khi ghi đè nó? Câu hỏi hay! Tôi nhận thấy ai đó đã phản đối - Tôi tự hỏi tại sao?
NickJ

3
Tôi muốn Threadlớp học là cuối cùng. Hàm khởi tạo đã có sẵn một runnable, vì vậy không cần kế thừa để tạo một luồng.
sang phải

Câu trả lời:


50

Tại sao lớp Thread được triển khai như một lớp thông thường chứ không phải là một lớp trừu tượng với phương thức run () là trừu tượng.

Câu hỏi này thực sự rút ra một thực tế là bạn nên luôn thích sáng tác hơn là thừa kế.

Nếu Threadlớp được khai báo là abstract, ngôn ngữ sẽ phải cung cấp một lớp khác mở rộng từ nó mà người lập trình có thể sử dụng để tạo ra Thread. Câu hỏi của bạn sau đó sẽ là về việc tại sao lớp này mà extendstừ Threadkhông phải là abstract. Nếu ngôn ngữ không cung cấp một lớp khác mà extendstừ đó Thread, các lập trình viên sẽ phải tạo lớp riêng của họ extendtừ đó Threadvà ghi đè run()phương thức.

Nếu không, tại sao phương thức không được khai báo cuối cùng trong lớp Thread ??

Lời giải thích khả thi duy nhất mà tôi có thể đưa ra là các nhà phát triển ngôn ngữ đã thấy một số trường hợp sử dụng để ghi đè startkhi lớp được giới thiệu với JDK. Phiên bản Java đầu tiên mà tôi sử dụng là 1.5 và cá nhân tôi chưa gặp trường hợp sử dụng nào mà tôi thấy cần phải ghi đè start. Như JB Nizet đã nói trong câu trả lời của mình

nếu Java được thiết kế lại từ đầu ngày hôm nay, rất có thể thiết kế sẽ khác


1
Mặc dù bạn không cần phải mở rộng lớp Thread để đặt tên chủ đề (chỉ cần gọi Thread.setName () sẽ làm gì), phần giải thích tại sao các lớp không thể trừu tượng là rất rõ ràng và chính xác ...
Codebender

@Codebender Đồng ý. Tôi đã xóa điều đó khỏi câu trả lời của mình vì nó không phải là một ví dụ điển hình và cũng vì câu hỏi của bạn không hỏi Khi nào thì mở rộng chuỗi.
CKing

Đó là một thiết kế sạch sẽ; tại sao nó sẽ khác ngày hôm nay?
Warren Dew

76

Tất nhiên bạn có thể chọn tự bắn vào chân mình, nhưng điều đó không có nghĩa là bạn phải.

Tại sao lớp Thread được triển khai như một lớp thông thường chứ không phải là một lớp trừu tượng với phương thức run () là trừu tượng.

Bởi vì cách được khuyến nghị để tạo bắt đầu một luồng là không phân lớp Chủ đề. Cách được khuyến nghị là xác định a Runnablevà chuyển nó làm đối số cho hàm tạo Chủ đề:

Runnable r = new Runnable() {
    @Override
    public void run() {
        ...
    }
};
Thread t = new Thread(r);
t.start();

Và do đó tôi đoán từ khóa cuối cùng sẽ phù hợp với phương pháp này hơn bất kỳ phương pháp nào khác.

Có và không. Bạn không thể thay thế việc triển khai start () bằng cách triển khai của riêng mình, nhưng bạn có thể thực hiện những việc bổ sung trong start () nếu bạn muốn:

@Override
public void start() {
    System.out.println("Did anyone tell you I will spawn a new thread??");
    super.start();
}

Điều đó nói rằng, nếu Java được thiết kế lại từ đầu ngày hôm nay, thì rất có thể thiết kế sẽ khác. Hãy nhớ rằng lớp này có từ Java 1.0 và vẫn tương thích ngược.


Tôi đã trích dẫn bạn trong câu trả lời của tôi. Hy vọng điều đó ổn với bạn :)
CKing

11
Không vấn đề gì. Nó là mã nguồn mở.
JB Nizet

2
@NayukiMinase không. Các giao diện luôn ở trong ngôn ngữ. Runnable tồn tại kể từ JDK 1.0.
JB Nizet

Rất tiếc, tôi đã nhầm lẫn với cách một hệ thống xử lý sự kiện AWT mới dựa trên lệnh gọi lại giao diện (thay vì ghi đè phương thức) được giới thiệu trong 1.1.
Nayuki

1
Chủ đề có một thiết kế sạch sẽ; tại sao nó sẽ khác ngày hôm nay?
Warren Dew

40

Tại sao Thread.start()không final?

Bạn có chắc mình sẽ không bao giờ muốn ghi đè nó không?

Class MyThreadFactory implements ThreadFactory {
    @Override
    public Thread newThread(Runnable r) {
        return new Thread(r) {
            @Override
            public void start() {
                LOGGER.info("Starting thread " + this);
                super.start();
            }
        };
    }
}

0

Tôi cảm thấy một số ký tự chân dung nên được đăng để có câu trả lời:

"Bạn có chắc là bạn sẽ không bao giờ muốn ghi đè nó không?"

Không! Tôi sẽ không. Giống như nói, "Bạn có chắc chắn, bạn muốn khai báo biến này là private?" Bởi vì nếu tôi có thể khai báo bất kỳ biến nào tôi sử dụng là công khai mà không sợ rằng các nhà phát triển khác có thể làm rối loạn logic thiết kế của tôi, thì việc viết mã sẽ trở nên dễ dàng. Một trong những mục đích quan trọng nhất của các khái niệm OOPS về Phạm vi, tính trừu tượng, tính đa hình, Xử lý lỗi, v.v. là truyền đạt cho các nhà phát triển khác về thiết kế và ý định đằng sau mã của bạn. để sử dụng super.start (). @Codebender đã viết phương thức bắt đầu cho một luồng mà không sử dụng super.start () và trình biên dịch chưa bao giờ bị phàn nàn. Vì vậy, anh ta có thể tự do phá vỡ toàn bộ cơ chế của Threading và trình biên dịch được cho là chỉ để nó đi qua? Phương thức bắt đầu của luồng đảm bảo rằng phương thức chạy được gọi và thực thi vào đúng thời điểm! Nó rất quan trọng đối với khái niệm Threading. Tôi sẽ rất vui nếu được sửa chữa, nếu tôi bỏ sót điều gì đó ở đây. 2.

Bởi vì cách được khuyến nghị để tạo một bắt đầu một luồng không phải là phân lớp Chủ đề.

Được rồi, nếu người viết mã được thiết kế cho phép, phân loại Thread và làm rối tung phương thức bắt đầu, Theo logic đó, chính thiết kế sẽ tự chụp trong chân. Theo một tuyên bố khác được đưa ra, (mà tôi hoàn toàn đồng ý), Runnable là cách được đề xuất. Vậy thì tại sao chúng ta lại cho phép lớp Thread khởi tạo luồng mà không có hạn chế? Tiếp theo là:

Bạn không thể thay thế việc triển khai start () bằng cách triển khai của riêng bạn,

Điều đó thực sự ủng hộ tuyên bố của người viết mã rằng phương thức bắt đầu phải là phương thức cuối cùng khi bạn nói điều đó .. ^

Một điểm, điều đó hợp lệ, đã được đề cập như một lưu ý phụ, nhưng là câu trả lời thực sự cho câu hỏi này

"Tương thích ngược".

Trên thực tế, các cải tiến thậm chí còn diễn ra muộn nhất là JDK 5.0, khi chúng kết hợp nhiều bổ sung chính và làm rõ cho mô hình đồng thời Java. Chúng tôi muốn khả năng tương thích ngược được hỗ trợ theo mọi cách và đó là lý do tại sao lớp Thread vẫn chính xác như trước đây, mặc dù giao diện Runnable là cách được khuyến nghị hiện nay.

Một lần nữa, tôi sẽ rất vui khi được sửa chữa, nếu tôi bỏ sót điều gì đó.

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.