Должен ли интерфейс, унаследованный от базового класса, быть явно реализован в подклассе? - PullRequest
13 голосов
/ 12 декабря 2008

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

Например, если вы хотите написать класс, который выполняет контракт интерфейса java.util.List. Вы реализуете это, расширяя класс java.util.AbstractList, который уже реализует интерфейс List. Вы прямо заявляете, что реализуете List?

public class MyList extends AbstractList implements List

Или вы сохраняете набор текста неявным способом?

public class MyList extends AbstractList

Какой способ считается лучшим стилем? Какие причины вы предпочитаете так или иначе? В каких ситуациях вы бы предпочли способ 1 или 2?

Ответы [ 11 ]

12 голосов
/ 12 декабря 2008

Избегайте избыточности. Используйте метод 2.

Используйте @Override для переопределений.

7 голосов
/ 12 декабря 2008

Это не вопрос стиля. Если вы расширяете AbstractList, который уже реализует List, вам не нужно явно реализовывать List.

Это не просто вопрос сохранения набора текста. Поскольку дополнительный «список реализации» является излишним, кто-то может потратить некоторое время, пытаясь выяснить, почему он там есть. По сути, вы пишете с гарантией, которая уже является частью языка.

4 голосов
/ 13 декабря 2008

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

В конце концов я решил, что жесткое правило в этом не имеет смысла - лучше использовать лучшее суждение относительно того, что я хотел сообщить.

2 голосов
/ 12 февраля 2009

Если вы заботитесь о том, чтобы пользователь вашего класса мог получить список реализованных интерфейсов через Class.getInterfaces (), тогда ваш класс должен упомянуть все интерфейсы в своем списке реализаций. Class.getInterfaces () не будет возвращать интерфейсы, реализованные суперклассами. Следующая программа напечатает 1, а затем 0.

import java.io.Serializable;

class Test implements Serializable {
    class Inner extends Test {
    }

    public static void main(String[] argv) throws Exception {
        System.out.println(Test.class.getInterfaces().length);
        System.out.println(Inner.class.getInterfaces().length);
    }
}
1 голос
/ 30 сентября 2014

Хотя это избыточно для реализации интерфейса в подклассе, но это хорошая практика для его реализации. Потому что кто-то может удалить реализованный интерфейс из суперкласса.

1 голос
/ 14 декабря 2008

Хотя я, как правило, придерживаюсь варианта 2, я продолжу в том же духе и докажу, что вариант 1 действительно уместен в большинстве случаев, когда он применяется.

Удобство и понятность кода важны. Вопрос, который мы должны задать себе, заключается в том, будет ли разработчик, использующий или видящий ссылку на этот класс, интуитивно понимать, что класс реализует этот интерфейс (и, таким образом, по существу является подтипом).

В хорошо написанном классе в типичном случае имя класса должно показывать, что он реализует интерфейс. Добавление интерфейса будет просто избыточностью.

Однако , в случаях, когда имя класса не дает понять, какие интерфейсы он реализует, и, несмотря на запах, это так, как есть, и это имя, которое застрянет, тогда имеет смысл добавить агрегаты для явного указания интерфейса. Это также хорошо, если кто-то в будущем изменит иерархию, что возможно при неинтуитивном наследовании, подобном этому.

1 голос
/ 12 декабря 2008

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

Чтобы ответить на общий вопрос, единственный случай, который я могу придумать для формулировки обоих, был бы, если бы вы расширили (скажем) AbstractFrotzer и реализовали Frobner, AbstractFrotzer реализовал Frobner, но у вас была причина полагать, что это может произойти не в будущем. , Если AbstractFrotzer действительно абстрактный, он может даже не реализовать все методы Фробнера. Это означает, что кто-то может изменить контракт на AbstractFrotzer без необходимости менять свой класс или любой другой код, который полагается на то, что ваш класс является Frobner. Я, однако, считаю, что это очень редкое обстоятельство, и даже если бы это произошло, и ваш класс только расширил AbstractFrotzer, было бы достаточно легко дать ему быструю семантическую проверку и, если необходимо, добавить к нему элемент Implements в этот момент.

1 голос
/ 12 декабря 2008

Я бы сказал более дословно. Зачем заставлять других, которые читают ваш код, искать то, что реализует класс? Это дает понять о наследуемой функциональности вашего класса. Но я бы постарался сохранить согласованность всего кода в проекте. Это будет сбивать с толку, если вы будете делать это только в одном месте, но если вы будете делать это последовательно, они будут знать об избыточности.

Примечание: в Java так много классов, и кто их знает? Более того, кто знает, из каких классов реализуется каждый класс? Если вы наберете лишнюю пару секунд, ваш разработчик сэкономит минуту или два классных занятия.

0 голосов
/ 12 февраля 2009

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

Что если базовый класс реализует несколько интерфейсов? Затем вы явно реализуете все эти интерфейсы и полностью резервируете цепочку наследования? Очевидно, нет, поэтому я считаю, что несоответствие этого подхода оказывается неправильным. Намерение может быть передано разными способами, и это не лучший подход, ИМХО.

0 голосов
/ 14 декабря 2008

Как правило, используйте вариант 2, а не вариант 1.

Вариант 1 не предоставляет никаких дополнительных преимуществ разработчику или кому-либо, кто собирается поддерживать код, в подавляющем большинстве случаев.

Если кто-то действительно хочет знать все корни определенного класса, любая IDE должна сделать это для вас, прямо. (Затмение: Ctrl + Shift + G)

Что если AbstractList решит больше не реализовывать List? Этого не произойдет для подавляющего большинства общих классов. А как насчет других менее очевидных (менее заслуживающих доверия )? Конечно, могут быть исключения, но очень мало (<1%?) </p>

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...