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

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

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

public class MyList extends AbstractList implements List

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

public class MyList extends AbstractList

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

12.12.2008 13:45:58
Я думаю, что это отличный вопрос.
Uri 13.12.2008 22:12:11
С точки зрения удобства использования, это может иметь смысл, если из имени класса не очевидно, что он реализует интерфейс.
Uri 13.12.2008 22:16:29
11 ОТВЕТОВ
РЕШЕНИЕ

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

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

4
27.08.2009 13:56:33

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

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

12
12.12.2008 13:47:27

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

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

7
12.12.2008 13:48:31
Что произойдет, если кто-то еще изменит AbstractList, чтобы больше не реализовывать List? Тогда MyList не работает? Я не думаю, что это сильный аргумент, а просто игра адвокат дьявола.
Alex Miller 13.12.2008 04:08:49
Ну, тогда, в любом случае это сломано. Если вы хотели реализовать AbstractList так же, как List, вы имели в виду нечто, что на самом деле ничего не значит, когда List уже реализует AbstractList. Если кто-то позже придет и изменит List, чтобы он больше не реализовывал AbstractList, то, конечно, ваши дополнительные «средства» что-то означают, но буквально по определению это не то, чье значение вы могли бы понять или намереваться, когда изначально писали свой List в первое место.
Steve B. 8.03.2017 22:13:16

Когда вы расширяете AbstractList, MyList уже имеет тип «List», поэтому нет необходимости подробно описывать (или реализовывать) интерфейс.

0
12.12.2008 13:50:07

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

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

1
12.12.2008 13:55:21

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

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

1
12.12.2008 15:43:25

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

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

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

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

0
13.12.2008 22:09:37

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

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

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

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

1
13.12.2008 22:15:35

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

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

0
11.02.2009 21:35:49

Если вы заботитесь о том, чтобы пользователь вашего класса мог получить список реализованных интерфейсов через 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);
    }
}
2
12.02.2009 00:25:36

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

1
30.09.2014 14:53:50