Защищено в интерфейсах
Почему все методы в interface
определение неявно public
? Почему это не позволяет protected
метод?
18 ответов
Потому что интерфейс должен означать "то, что вы можете видеть вне класса". Не имеет смысла добавлять закрытые методы.
Хотя часто цитируемая причина заключается в том, что "интерфейсы определяют публичные API", я думаю, что это упрощение. (И это тоже "пахнет" круговой логикой.)
Вложенный интерфейс может быть защищенным или закрытым, и в этом случае он вообще не определяет открытый интерфейс.
Было бы бессмысленно иметь интерфейсы, которые имеют смесь модификаторов доступа; например, частично открытый и частично ограниченный другими классами в том же пакете, что и интерфейс. На самом деле, в некоторых случаях это может быть совершенно полезным, IMO.
На самом деле, я думаю, что одним из доводов в пользу неявной открытости членов интерфейса является то, что он делает Java проще:
Неявно члены открытого интерфейса проще иметь дело с программистами. Сколько раз вы видели код (классы), где модификаторы доступа к методу выбирались, казалось бы, случайно? Многим "обычным" программистам трудно понять, как лучше всего управлять границами абстракции Java 1. Добавление public / protected / package к интерфейсам делает их еще сложнее.
Неявно открытые члены интерфейса упрощают спецификацию языка... и, следовательно, задачу для авторов компиляторов Java и тех, кто реализует API Reflection.
Эта линия мышления делает "интерфейсы, определяющие публичные API" следствием (или характеристикой) языкового дизайна... а не наоборот. В действительности, эти две мысли, вероятно, развивались параллельно в умах дизайнеров Java.
1 - Конечно, у программистов высшего класса нет проблем с этими вещами, и они могут приветствовать более богатую палитру функций контроля доступа. Но что происходит, когда их код передается кому-то другому для поддержки?
Я должен сказать, что этот вопрос был вновь открыт введением методов по умолчанию в Java 8. Проект, над которым я сейчас работаю, похож на базовую природу интерфейса и предназначен для отвлечения намерения от реализации.
Есть несколько случаев, когда я мог бы радикально упростить свой код с помощью метода "по умолчанию защищен". Оказывается, что это на самом деле не работает, поскольку интерфейсы все еще придерживаются логики Java 7. Обычный защищенный метод не имеет особого смысла по причинам, упомянутым выше; но если общедоступному методу по умолчанию требуется низкоуровневый ресурс, который вряд ли изменится и может быть предоставлен защищенным методом, мне кажется, что работа "по умолчанию защищена" не только поддержит более чистый код, но и защитит будущих пользователей от случайные злоупотребления.
(Это, к сожалению, не меняет того факта, что мне все еще нужно чрезмерно усложнять мой код ненужными в противном случае аннотациями; но я намерен добавить запрос о возможностях в Oracle.)
В нескольких ответах здесь используется круговое рассуждение, чтобы объяснить, почему методы интерфейса не могут быть защищены: это потому, что они должны быть общедоступными, поэтому, очевидно, они не могут быть защищены!
Это ничего не объясняет, но, к счастью, пару лет назад кто-то поднял запрос на улучшение защищенных методов в интерфейсах как ошибку JDK, которая проливает свет на проблему:
Защищенные методы в интерфейсах: совместное использование пакетов
Поскольку модификаторы в Java немного ограничены, способ совместного использования методов в пакетах ограничен общедоступными методами. Иногда делать метод общедоступным бывает опасно, но это необходимо из-за отсутствия соответствующих модификаторов. Мое решение преодолевает это ограничение.
Спецификация языка java в настоящее время не допускает использование модификатора protected для методов интерфейса. Мы можем воспользоваться этим фактом и использовать методы интерфейса protected для этой новой функции.
Если метод интерфейса помечен как защищенный, а интерфейс реализован классом в другом пакете, метод не должен быть общедоступным, но также может быть частным или, по крайней мере, защищенным пакетом. Метод является видимым, независимо от того, что класс объявляет, и дополнительно видимым в исходном пакете интерфейса (и подпакетах?).
Таким образом, мы могли использовать определенные методы в хорошо известных пакетах.
И это ответ на запрос улучшения, который был закрыт статусом Won't fix
:
Это предложение пытается решить проблему таким образом, чтобы добавить сложности и особые случаи с небольшой реальной прибылью. Типичный способ решить эту проблему - создать частный класс, реализующий общедоступный интерфейс. Методы реализации являются общедоступными, но находятся в частном классе, поэтому они остаются частными.
Альтернатива, доступная начиная с Java 9, - сделать классы и методы общедоступными, но в пределах модуля, который имеет квалифицированный экспорт в определенные "дружественные" модули, а не для широкой публики.
Итак, авторитетные выводы из этого отчета об ошибке:
- Текущая ситуация не изменится; интерфейсы вряд ли когда-либо будут поддерживать
protected
методы. - Обоснование отказа от поддержки
protected
методов в интерфейсах заключается в том, что они "добавляют сложности и особые случаи для небольшого реального выигрыша". - Начиная с Java 9 существует альтернативный подход для предоставления доступа к методам на уровне пакета. Используйте Систему модулей платформы Java (JPMS), чтобы "сделать классы и методы общедоступными, но в пределах модуля, у которого есть квалифицированный экспорт в определенные" дружественные "модули вместо экспорта для широкой публики".
Потому что интерфейсы определяют публичные API. Все, что защищено, является внутренней деталью, которая не принадлежит интерфейсу.
Вы можете использовать абстрактные классы с защищенными абстрактными методами, но интерфейсы ограничены открытыми методами и открытыми статическими конечными полями.
Я твердо считаю, что интерфейсы должны позволять защищенные методы; кто сказал, что интерфейсы должны быть видны всем в мире? Что касается вашей точки зрения, это может сбить с толку "обычных" (читай: некомпетентных) программистов: большая часть ООП связана с правильной структурой объектов, классов, пакетов и т. Д., Если программисту сложно сделать все это правильно, у него есть гораздо большая проблема. Java была создана для такого типа вещей.
Возможно, потому что это интерфейс, то есть он говорит клиентам, что они могут делать с экземплярами, а не говорит им, что они не могут делать.
Интерфейс предназначен для использования в качестве "контракта" для внешнего мира. Если вы хотите использовать защищенные методы, вам, вероятно, лучше использовать абстрактный класс (если, конечно, он доступен в Java). Wiki
Кроме того, у этого поста, вероятно, также есть отличные ответы: почему я не могу защитить членов интерфейса?
Поскольку реализующий класс должен реализовывать ВСЕ методы, объявленные в вашем интерфейсе, что произойдет, если ваш реализующий класс будет в другом пакете?
Объявление внутренних подынтерфейсов - это хорошая практика, но вы не можете объявить свои внутренние методы как protected
в интерфейсе на Java, технически.
Конечно, вы можете создать другой интерфейс для внутреннего использования, который расширяет публичный интерфейс:
public interface YourPublicInterface {
public void doThing1();
public void doThing2();
public void doThing3();
interface Internal extends YourPublicInterface {
void doAnyInternalThing1();
void doAnyInternalThing2();
}
}
Вы можете использовать Internal
интерфейс внутри пакета, но вы должны принять любой подтип YourPublicInterface
(публичными методами):
public class AClassInYourPackage {
public void someMethod(YourPublicInterface param) {
if (param instanceof YourPublicInterface.Internal) {
// run the optimized code
} else {
// run the general code
}
}
}
Вне пакета пользователи могут использовать YourPublicInterface
без проблем.
В общей практике программисты создают абстрактные классы в подобных ситуациях. Однако в этом случае мы теряем преимущества множественного наследования.
Интерфейс Если вы хотите использовать что-то, как вы описали, продолжайте с абстрактными классами или вложенными интерфейсами.
Выдержка из стиля кода о переменных интерфейса, но все же применима к методам:
Переменные интерфейса неявно общедоступны, потому что интерфейсы предназначены для предоставления интерфейса прикладного программирования (API), который полностью доступен программистам Java для ссылок и реализации в их собственных приложениях. Поскольку интерфейс может использоваться в пакетах Java, которые отличаются от их собственных, общедоступная видимость гарантирует, что программный код может получить доступ к переменной.
Члены, как правило, используются участниками. Например,
AbstractList.removeRange(int, int)
используется
AbstractList.clear()
, и его переопределение улучшит производительность
clear
.
Если
protected
методы разрешены в интерфейсах, это означает, что большинство
public default
реализации будут полагаться на эти методы. Если подкласс не нужен
default
реализации и переопределить все методы, а также все не-
public
методы больше не будут полезны. Если они
abstract
, нам все равно придется их переопределить, что усложняет создание подклассов.
Защищенные методы всегда доступны подклассу, только если подкласс расширяет базовый класс.
В случае интерфейса подкласс никогда не расширяет интерфейс. Он реализует интерфейс.
Защищенные методы доступны через расширение, а не с помощью инструмента.
Я знаю, что это очень поздно / давно, и я уверен, что к настоящему времени у вас гораздо больше опыта, чем у меня, поэтому, если это кому-то поможет, ура, ...
Я нашел способ, который обеспечивает удобную связность и предотвращает использование объектов при работе с защищенными внутренними пакетами.
Оглядываясь назад, это кажется смехотворно простым, но суть в том, что цель того, чего вы хотите достичь с помощью фрагмента кода, становится в 10 раз сложнее, если вы потратите какое-то количество времени на попытки приспособить ее к стандартам и / или настраивается на возможность повторного использования кода.
Реальность такова, что порядок будет разворачиваться по мере вашего продвижения ..
Но я составил диаграмму, потому что она помогла мне в чем-то, что, в зависимости от проблемы, может легко меня сбить с толку.
public interface CommonPublics {
void publicInAll();
}
public static abstract class CommonProtecteds implements CommonPublics {
protected abstract void protectedInAll();
}
public interface MyObjectPublics {
void myObjectPublic();
}
public static class MyObject extends CommonProtecteds implements MyObjectPublics {
@Override
public void publicInAll() {
}
@Override
protected void protectedInAll() {
}
@Override
public void myObjectPublic() {
}
}
Теперь, когда вам нужно иметь дело с защищенными объектами в одном пакете, вместо того, чтобы иметь дело с MyObject, вы можете вместо этого иметь дело с CommonProtecteds.
Теоретически вы могли бы перенести любую логику, которая обрабатывает защищенные объекты, внутри абстрактного, и это зависит от вас, но если по какой-либо причине вы имеете дело с параллелизмом, лучше сохранить все как можно более жестко и как можно ближе к синхронизированному телу. поэтому все, что требуется защищенному для работы, что может потребоваться в разных заблокированных местах, лучше поместить в объект, который будет обрабатывать это.
Единственный сценарий, в котором это имеет смысл, - это когда вы хотите ограничить видимость одним и тем же пакетом. Все остальные виды использования protected
не применимы. В частности, protected
методы часто используются для предоставления доступа к некоторым деталям реализаций более низкого уровня для потомков. Но объявлять это в интерфейсе не имеет смысла, так как нет более низкоуровневой реализации.
И даже сценарий пакета не совсем то, что интерфейсы.
Чтобы достичь того, чего вы, вероятно, хотите, вам нужны два интерфейса: один для внутреннего использования, а другой - в открытом API. (С внутренним возможно, но не обязательно расширяет публичный.) Или, как другие указали, абстрактный суперкласс.
Хороший вопрос, если спросить как, почему разработчики сделали атрибуты интерфейса публичными незащищенными, ...?
Разрешение защищенного нарушило бы политику / свойство интерфейса: "любая предоставляемая вами абстракция должна быть доступна везде без каких-либо условий, но не должна позволять изменять ее (например: значение экземпляра var)"
Для такого случая использования вам следует избегать использования интерфейса.
Интерфейсы предназначены для демонстрации методов во внешнем мире. Таким образом, эти методы являются публичными по своей природе. Однако, если вы хотите ввести абстракцию в пределах одного и того же семейства классов, это можно сделать, создав другой уровень абстракции между вашим интерфейсом и классом реализации, то есть абстрактный класс. Пример демонстрируется ниже.
public interface MyInterface {
public void publicMethod(); // needs to be public
}
public abstract class MyAbstractClass implements MyInterface {
@Override
public void publicMethod() {
protectedMethod(); // you can call protected method here
// do other stuff
}
protected abstract void protectedMethod(); // can be protected
}
public class MyClass extends MyAbstractClass {
@Override
protected void protectedMethod() {
// implement protected method here, without exposing it as public
}
}
Члены интерфейса всегда модифицируются / реализуются вне себя (в классе или структуре), поэтому они неявно общедоступны, и модификаторы доступа с ним не допускаются. Даже члены интерфейса публично реализованы. Однако, если вы хотите сделать член закрытым в классе, который обеспечивает его реализацию, вам нужно явно реализовать интерфейс.
interface IMyIF {
int MyMeth(int x);
}
//then it is legal to implement IMyIF as shown here:
class MyClass : IMyIF {
int IMyIF.MyMeth(int x) {
return x / 3;
}
}
явная реализация дает вам возможность реализовать интерфейсный метод, чтобы он не был публичным членом класса, обеспечивающим реализацию.