Почему Thread не является абстрактным классом, а start () не является окончательным?

79

Почему Threadкласс был реализован как обычный класс, а не как абстрактный класс с run()абстрактным методом.

Возможно, это принесет какие-то проблемы? Или в этом есть какой-то смысл?

Кроме того, Thread.start()предполагается , что этот метод является очень специфическим методом , функциональность которого не может быть реализована никаким другим классом (если я не ошибаюсь). И поэтому я предполагаю, что finalключевое слово подходит для этого больше, чем для любого другого метода.

Но я могу переопределить этот метод и использовать его, как мне нравится,

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

Очевидно, он только напечатал,

Кто-нибудь сказал вам, что я создам новую ветку ??

Есть ли какой-нибудь смысл в переопределении, кроме как запутать заменяющего вас инженера?

Если нет, то почему метод не был объявлен как final в классе Thread?

Codebender
источник
Здесь вы вызываете метод start как член класса ... он не
вызывается
2
Для ясности - вы спрашиваете, почему Thread.run () не является абстрактным, поскольку пользователям необходимо переопределить его, и почему Thread.start () не является окончательным, поскольку нет никакого смысла переопределять его? Хороший вопрос! Я заметил, что кто-то проголосовал против - интересно, почему?
NickJ
3
Я бы предпочел, чтобы Threadурок был заключительным. Конструктор уже принимает исполняемый объект, поэтому для создания потока наследование не требуется.
правый фолд 04

Ответы:

50

Почему класс Thread был реализован как обычный класс, а не как абстрактный класс с абстрактным методом run ().

На самом деле этот вопрос сводится к тому, что вы всегда должны предпочесть композицию наследованию.

Если бы Threadкласс был объявлен как abstract, язык должен был бы предоставить другой класс, расширяющий его, который программисты могли бы использовать для создания Thread. Ваш вопрос будет затем о том, почему этот класс extendsот Threadне abstract. Если бы в языке не было другого класса, extendsиз которого происходило бы Thread, программистам пришлось бы создать свой собственный класс, extendиз которого был бы, Threadи переопределить run()метод.

Если нет, то почему метод не был объявлен как final в классе Thread ??

Единственное возможное объяснение, которое я могу дать, заключается в том, что разработчики языка видели некоторые варианты использования для переопределения, startкогда класс был представлен в JDK. Первой версией Java, которую я использовал, была 1.5, и я лично не встречал варианта использования, в котором я обнаружил бы необходимость переопределения start. Как заявил Дж. Б. Низет в своем ответе

если бы Java была переработана с нуля сегодня, велика вероятность, что дизайн был бы другим

CKing
источник
1
Хотя вам не нужно расширять класс Thread для именования потока (подойдет просто вызов Thread.setName ()), часть, объясняющая, почему класс не может быть абстрактным, очень ясна и точна ...
Codebender
@Codebender Согласен. Я удалил это из своего ответа, поскольку это не был хороший пример, а также потому, что в вашем вопросе не спрашивалось, когда расширять поток.
CKing
Чистый дизайн; почему сегодня было бы иначе?
Уоррен Дью,
76

Вы, конечно, можете выстрелить себе в ногу, но это не значит, что вы должны это делать.

Почему класс Thread был реализован как обычный класс, а не как абстрактный класс с абстрактным методом run ().

Поскольку рекомендуемый способ создания запуска потока - это не создание подкласса Thread. Рекомендуемый способ - определить a Runnableи передать его в качестве аргумента конструктору Thread:

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

И поэтому я предполагаю, что ключевое слово final подходит для этого больше, чем для любого другого метода.

Да и нет. Вы не можете заменить реализацию start () своей собственной реализацией, но вы можете делать дополнительные вещи в start (), если хотите:

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

Тем не менее, если бы Java была переработана с нуля сегодня, велика вероятность, что дизайн был бы другим. Помните, что этот класс восходит к Java 1.0 и по-прежнему имеет обратную совместимость.

JB Nizet
источник
Я процитировал вас в своем ответе. Надеюсь, вас это устраивает :)
CKing
11
Нет проблем. Это открытый исходный код.
JB Nizet
2
@NayukiMinase нет. Интерфейсы всегда были на языке. Runnable существует с JDK 1.0.
JB Nizet
К сожалению, я запутался в том, как новая система обработки событий AWT, основанная на обратных вызовах интерфейса (а не на переопределении методов), была представлена ​​в 1.1.
Наюки
1
Тема имеет чистый дизайн; почему сегодня все должно быть иначе?
Уоррен Дью,
40

Почему Thread.start()нет final?

Вы уверены , что никогда не захотите его отменять?

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

Я чувствую, что для ответов нужно опубликовать несколько калибровок:

"Вы уверены, что никогда не захотите отменить это?"

Нет! Я не стал бы. Это все равно что сказать: «Вы уверены, что хотите объявить эту переменную частной?» Потому что, если бы я мог объявить любую переменную, которую я использую, как общедоступную, не опасаясь, что другие разработчики могут испортить мою логику проектирования, кодирование было бы легким делом. Одна из наиболее важных целей концепций OOPS, таких как Область видимости, абстракция, полиморфизм, обработка ошибок и т. Д., Состоит в том, чтобы сообщить другим разработчикам дизайн и намерения, стоящие за вашим кодом. Как указано в вопросе, когда вы переопределяете метод запуска, никто вас не заставляет использовать super.start (). @Codebender написал метод запуска потока без использования super.start (), и компилятор никогда не жаловался. Значит, он может сломать весь механизм Threading, а компилятор может просто позволить ему пройти? Метод запуска потока гарантирует, что метод запуска вызывается и выполняется в нужное время! Это очень важно для самой концепции Threading. Я был бы более чем счастлив, если бы я что-то здесь пропустил. 2.

Поскольку рекомендуемый способ создания запуска потока - это не создание подкласса Thread.

Хорошо, если кодовый бендер разрешен дизайном, чтобы подклассифицировать Thread и испортить метод запуска. По этой логике, дизайн стреляет себе в ногу. Согласно другому заявлению (с которым я полностью согласен), Runnable является рекомендуемым способом. Тогда почему мы вообще позволяем классу Thread создавать экземпляр потока без ограничений? Далее следуют:

Вы не можете заменить реализацию start () своей собственной реализацией,

Это на самом деле подтверждает утверждение codebender, что метод start должен быть окончательным, когда вы говорите это ... ^

Единственный действительный момент уже упоминается как побочное примечание, но это фактический ответ. на этот вопрос.

"Обратная совместимость".

Фактически, улучшения даже были внесены в JDK 5.0, когда они включали множество важных дополнений и уточнений в модель параллелизма Java. Мы хотим, чтобы обратная совместимость поддерживалась полностью, и именно поэтому класс Thread остается таким же, как и раньше, хотя в настоящее время рекомендуется использовать интерфейс Runnable.

Опять же, я был бы более чем счастлив, если бы меня поправили, если я что-то упустил.

Уткарш Шривастава
источник