Dlaczego Thread nie jest klasą abstrakcyjną, a funkcja start () nie jest ostateczna?

79

Dlaczego Threadklasa została zaimplementowana jako zwykła klasa, a nie klasa abstrakcyjna z run()metodą abstrakcyjną.

Czy może to spowodować jakieś problemy? A może ma to jakiś sens?

Ponadto Thread.start()metoda ma być bardzo specyficzną metodą, której funkcjonalności nie może zaimplementować żadna inna klasa (jeśli się nie mylę). Dlatego myślę, że to finalsłowo kluczowe byłoby bardziej odpowiednie do tego niż jakiejkolwiek innej metody.

Ale mogę zmienić tę metodę i używać jej tak, jak lubię,

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??");
    }
}

To oczywiście tylko wydrukowane,

Czy ktoś ci powiedział, że utworzę nowy wątek?

Czy ma sens zastępowanie innych niż dezorientowanie zastępującego cię inżyniera?

Jeśli nie, dlaczego metoda nie została zadeklarowana jako ostateczna w klasie Thread?

Codebender
źródło
Tutaj wywołujesz metodę start jako członek klasy ... nie jest wywoływana z harmonogramu ...
CoderNeji
2
Dla jasności - pytasz, dlaczego Thread.run () nie jest abstrakcyjny, ponieważ użytkownicy muszą go przesłonić, i dlaczego Thread.start () nie jest ostateczny, skoro nie ma sensu go przesłonić? Dobre pytanie! Zauważyłem, że ktoś przegłosował - zastanawiam się, dlaczego?
NickJ,
3
Wolałabym, żeby Threadzajęcia były ostateczne. Konstruktor już przyjmuje plik runnable, więc dziedziczenie nie jest potrzebne do utworzenia wątku.
prawy bok

Odpowiedzi:

50

Dlaczego klasa Thread została zaimplementowana jako zwykła klasa, a nie klasa abstrakcyjna z metodą run () będącą abstrakcyjną.

To pytanie sprowadza się do tego, że zawsze powinieneś przedkładać kompozycję nad dziedziczenie.

Gdyby Threadklasa została zadeklarowana jako abstract, język musiałby zapewnić inną klasę, która jest z niego rozszerzana, której programiści mogliby użyć do utworzenia Thread. Twoje pytanie brzmiałoby wtedy, dlaczego ta klasa, extendsz której Threadpochodzi, nie jest abstract. Gdyby język nie zapewniał innej klasy, extendsz której pochodzi Thread, programiści musieliby stworzyć własną klasę, która extendpochodzi z, Threadi przesłonić run()metodę.

Jeśli nie, dlaczego metoda nie została uznana za ostateczną w klasie Thread?

Jedynym możliwym wyjaśnieniem, jakie mogę dać, jest to, że programiści języka widzieli kilka przypadków użycia do przesłonięcia, startgdy klasa została wprowadzona do JDK. Pierwsza wersja Javy, której użyłem, to 1.5 i osobiście nie natknąłem się na przypadek użycia, w którym znalazłem potrzebę przesłonięcia start. Jak stwierdził JB Nizet w swojej odpowiedzi

gdyby Java została dziś przeprojektowana od zera, istnieje duża szansa, że ​​projekt byłby inny

CKing
źródło
1
Chociaż nie musisz rozszerzać klasy Thread w celu nazwania wątku (wystarczyłoby wywołanie Thread.setName ()), część wyjaśniająca, dlaczego klasa nie może być abstrakcyjna, jest bardzo przejrzysta i precyzyjna ...
Codebender
Zgoda @Codebender. Usunąłem to z mojej odpowiedzi, ponieważ nie był to dobry przykład, a także dlatego, że twoje pytanie nie pytało, kiedy przedłużyć wątek.
CKing
To czysty projekt; dlaczego miałoby być dzisiaj inaczej?
Warren Dew
76

Możesz oczywiście strzelić sobie w stopę, ale to nie znaczy, że musisz.

Dlaczego klasa Thread została zaimplementowana jako zwykła klasa, a nie klasa abstrakcyjna z metodą run () będącą abstrakcyjną.

Ponieważ zalecanym sposobem tworzenia początku wątku nie jest podklasa Thread. Zalecanym sposobem jest zdefiniowanie a Runnablei przekazanie go jako argumentu do konstruktora Thread:

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

Dlatego myślę, że ostatnie słowo kluczowe byłoby bardziej odpowiednie do tego niż jakiejkolwiek innej metody.

Tak i nie. Nie możesz zastąpić implementacji start () własną implementacją, ale możesz zrobić dodatkowe rzeczy w start (), jeśli chcesz:

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

To powiedziawszy, gdyby Java została dziś przeprojektowana od zera, istnieje duża szansa, że ​​projekt byłby inny. Pamiętaj, że ta klasa pochodzi z języka Java 1.0 i nadal jest kompatybilna wstecz.

JB Nizet
źródło
Zacytowałem cię w mojej odpowiedzi. Mam nadzieję, że nie masz
nic przeciwko
11
Nie ma problemu. To open-source.
JB Nizet
2
@NayukiMinase no. Interfejsy zawsze były w języku. Runnable istnieje od JDK 1.0.
JB Nizet
Ups, zdezorientowałem się, w jaki sposób nowy system obsługi zdarzeń AWT oparty na wywołaniach zwrotnych interfejsu (zamiast nadpisywania metod) został wprowadzony w wersji 1.1.
Nayuki
1
Wątek ma czysty wygląd; dlaczego miałoby być dzisiaj inaczej?
Warren Dew
40

Dlaczego Thread.start()nie final?

Czy na pewno nigdy nie chciałbyś go zastąpić?

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();
            }
        };
    }
}
Solomon Slow
źródło
0

Uważam, że w odpowiedziach należy opublikować kilka kalirifkacji:

"Czy na pewno nie chciałbyś tego zmienić?"

Nie! Wolałbym nie. To tak, jakby powiedzieć: „Czy na pewno chcesz zadeklarować tę zmienną jako prywatną?” Ponieważ gdybym mógł zadeklarować dowolną zmienną, której używam jako publiczną, bez obawy, że inni programiści mogą zepsuć moją logikę projektowania, kodowanie byłoby proste. Jednym z najważniejszych celów OOPS koncepcji zakresu, abstrakcji, polimorfizmu, obsługi błędów itp. Jest przekazanie innym programistom projektu i intencji kryjących się za Twoim kodem. użyć super.start (). @Codebender napisał metodę startową dla wątku bez użycia super.start (), a kompilator nigdy nie narzekał. Więc może złamać cały mechanizm wątkowości, a kompilator ma po prostu pozwolić mu przejść? Metoda startowa wątku zapewnia, że ​​metoda run jest wywoływana i wykonywana we właściwym czasie! Ma to kluczowe znaczenie dla samej koncepcji Threading. Byłbym bardziej niż szczęśliwy, gdybym został poprawiony, gdybym coś tutaj przeoczył. 2.

Ponieważ zalecanym sposobem tworzenia początku wątku nie jest podklasa Thread.

OK, jeśli projektant pozwala na podbijanie wątku i zepsucie metody startowej, zgodnie z tą logiką projekt strzela sam sobie w stopę. W innym oświadczeniu (z którym całkowicie się zgadzam) Runnable jest zalecanym sposobem. Dlaczego więc w ogóle pozwalamy klasie Thread na tworzenie wystąpienia wątku w ogóle bez żadnych ograniczeń? Następnie następuje:

Nie możesz zastąpić implementacji start () własną implementacją,

To faktycznie potwierdza twierdzenie codebender, że metoda start powinna być ostateczna, kiedy to mówisz .. ^

Jedna kwestia, która jest ważna, została już wspomniana na marginesie, ale jest rzeczywistą odpowiedzią na to pytanie

"Kompatybilność wsteczna".

W rzeczywistości ulepszenia wprowadzano nawet dopiero w JDK 5.0, kiedy obejmowały wiele ważnych dodatków i wyjaśnień do modelu współbieżności języka Java. Chcemy, aby w pełni obsługiwana była kompatybilność wsteczna i dlatego klasa Thread jest nadal dokładnie taka, jak była wcześniej, mimo że interfejs Runnable jest obecnie zalecany.

Znowu byłbym bardziej niż szczęśliwy, gdyby został poprawiony, gdybym coś przeoczył.

Utkarsh Srivastava
źródło