Переопределение закрытых методов в Java

Как кратко описано здесь, переопределение закрытых методов в Java недопустимо, потому что закрытые методы родительского класса являются "автоматически финалом, и скрытый от производного класса". Мой вопрос является в основном академическим.

То, как это не нарушение инкапсуляции, чтобы не позволить закрытому методу родителя быть "переопределенным" (т.е., реализовало независимо, с той же подписью, в дочернем классе)? К закрытому методу родителя не может получить доступ или наследовать дочерний класс, в соответствии с принципами инкапсуляции. Это скрыто.

Так, почему дочерний класс должен быть ограничен в реализации его собственного метода с тем же именем/подписью? Существует ли хорошая теоретическая основа для этого, или это - просто прагматическое какое-то решение? Другие языки (C++ или C#) имеют различные правила об этом?

58
задан Michael Myers 5 January 2010 в 23:08
поделиться

8 ответов

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

class Base
{
   private void foo()
   {
   }
}

class Child extends Base
{
    private void foo()
    {
    }
}

Обратите внимание, что если вы попытаетесь применить @Override аннотацию к Child.foo(), то получите ошибку во время компиляции. Пока ваш компилятор/IDE настроен на выдачу предупреждений или ошибок, если вы пропустили аннотацию @Override, все должно быть в порядке. Правда, я предпочитаю C# подход, в котором ключевым словом является override, но на Java это делать явно поздно.

Что касается обработки C# "переопределения" private-метода - private-метод не может быть вообще виртуальным, но в базовом классе, конечно же, можно внедрить новый private-метод с тем же именем, что и private-метод.

.
70
ответ дан 24 November 2019 в 18:50
поделиться

Когда метод является частным, он не виден ребенку. Поэтому нет смысла его переопределять.

0
ответ дан 24 November 2019 в 18:50
поделиться

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

Так почему же дочерний класс должен быть ограниченного в реализации собственного метод с тем же именем/подписью?

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

Переопределенные методы подвержены динамической диспетчеризации, т.е. фактически вызываемый метод выбирается во время выполнения в зависимости от фактического типа вызываемого объекта. С частным методом этого не происходит (и не должно происходить, согласно вашему первому утверждению). И именно это подразумевается под выражением "private методы не могут быть переопределены"

.
7
ответ дан 24 November 2019 в 18:50
поделиться

Я думаю, ты неправильно истолковываешь то, что написано в этом посте. Это не говорит о том, что дочерний класс "ограничен в реализации своего собственного метода с таким же именем/подписью"

Вот код, слегка отредактированный:

public class PrivateOverride {
  private static Test monitor = new Test();

  private void f() {
    System.out.println("private f()");
  }

  public static void main(String[] args) {
    PrivateOverride po = new Derived();
    po.f();
    });
  }
}

class Derived extends PrivateOverride {
  public void f() {
    System.out.println("public f()");
  }
}

И цитата:

Вы можете обоснованно ожидать, что результат будет "public f( )",

Причина этой цитаты в том, что переменная po на самом деле содержит экземпляр Derived. Однако, так как метод определен как private, компилятор на самом деле смотрит на тип переменной, а не на тип объекта. И он транслирует вызов метода в invokespecial (думаю, это правильный опкод, не проверил спецификацию JVM), а не в invkeinstance.

.
3
ответ дан 24 November 2019 в 18:50
поделиться

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

Так как инкапсуляция имеет отношение к поведению, а не к деталям реализации, то частные методы не имеют никакого отношения к инкапсуляции идеи. В некотором смысле, ваш вопрос не имеет смысла. Это как спросить "Как положить сливки в кофе, а не нарушить инкапсуляцию?"

Предположительно частный метод используется чем-то, что является публичным. Вы можете отменить это. При этом вы изменили свое поведение.

-1
ответ дан 24 November 2019 в 18:50
поделиться

Приношу свои извинения за неправильное и не соответствующее моему описанию использование термина "переопределить". Мое описание описывает сценарий. Следующий код расширяет пример Джона Скита для отображения моего сценария:

class Base {
   public void callFoo() {
     foo();
   }
   private void foo() {
   }
}

class Child extends Base {
    private void foo() {
    }
}

Использование похоже на следующее:

Child c = new Child();
c.callFoo();

Проблема, с которой я столкнулся, заключается в том, что вызывался родительский метод foo(), хотя, как видно из кода, я вызывал callFoo() на дочерней переменной экземпляра. Я думал, что определяю в функции Child() новый private метод foo(), который будет вызываться методом callFoo(), но думаю, что некоторые из сказанного kdgregory могут быть применимы к моему сценарию - возможно, из-за того, как конструктор производного класса вызывает super(), а возможно и нет.

В Eclipse не было предупреждения компилятора, и код действительно компилировался. Результат был неожиданным

.
0
ответ дан 24 November 2019 в 18:50
поделиться
[
] [

] "Имеют ли другие языки (C++ или C#) иные правила?" [

]. [
] [

] Ну, у C++ разные правила: статический или динамический процесс связывания членских функций и применение привилегий доступа ортогональны.[

]. [

]Придание функции-члену статуса []private[] modifier привилегий доступа означает, что эта функция может быть вызвана только ее декларирующим классом, а не другими (даже производными классами). Когда Вы объявляете функцию-член []private[] членом как []virtual[], даже виртуальную ([]virtual void foo() = 0;[]), Вы разрешаете базовому классу использовать привилегии доступа.[

]. [

] Когда речь заходит о функциях члена [] virtual[], привилегии доступа говорят вам, что вы должны делать: [

]. [
    ] [
  • ][]private virtual[] означает, что можно специализироваться на поведении, но вызов функции-член производится базовым классом, конечно же, управляемым способом[
  • ]. [
  • ][] protected virtual[] означает, что вы должны/должны вызывать версию функции-член верхнего класса, когда переопределяете ее[
  • ]. [
] [

] Таким образом, в языке С++ привилегии доступа и виртуальность не зависят друг от друга. Определение того, должна ли функция быть статически или динамически связана, является последним шагом в разрешении вызова функции.[

]. [

] Наконец, шаблон проектирования шаблонного метода должен быть предпочтительнее [] публичных виртуальных [] членских функций. [

] [

] Справка: []Conversations: Virtually Yours[][

] [

] В статье приводится практическое использование функции члена [] private virtual [] member.[

] [
] [

]ISO/IEC 14882-2003 §3.4.1 [

] [
] [

] Поиск имени может ассоциировать несколько деклараций с именем, если он находит, что имя является именем функции; говорят, что декларации формируют набор перегруженных функций (13.1). Разрешение перегрузки (13.3) происходит после успешного поиска имени. Правила доступа (п. 11) рассматриваются только после того, как поиск имени и разрешение перегрузки функции (если применимо) увенчались успехом. Только после поиска имени, разрешения перегрузки функции (если применимо) и проверки доступа, атрибуты, введенные объявлением имени, используются в дальнейшем при обработке выражений (п. 5). [

] [
] [

]ISO/IEC 14882-2003 §5.2.2 [

] [
] [

] Функция, вызываемая при вызове член-функции, обычно выбирается в соответствии со статическим типом выражения объекта (п. 10), но если эта функция является виртуальной и не указана с помощью аквалифицированного - тогда фактически вызываемая функция будет конечным переопределителем (10.3) выбранной функции в динамическом типе выражения объекта [Примечание: динамический тип - это тип объекта, на который указывает или на который ссылается текущее значение выражения объекта.[

]. [
]
16
ответ дан 24 November 2019 в 18:50
поделиться

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

  1. Допустим, есть частный метод boolean hasCredentials () , тогда расширенный класс может просто переопределить это так:

     логическое hasCredentials () {return true;  }
     

    , таким образом нарушив проверку безопасности.

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

    Это плохо: допустим, этого метода сначала не существует в Base . Теперь разработчик может законно создать класс Derived и дать ему метод hasCredentials , который работает должным образом.

    Но теперь выпущена новая версия исходного Базового класса. Его открытый интерфейс не изменяется (как и его инварианты), поэтому мы должны ожидать, что он не нарушит существующий код. Только так, потому что теперь есть конфликт имени с методом в производном классе.

Я думаю, что вопрос проистекает из недоразумения:

Как это / не / нарушение инкапсуляции не позволяет «переопределить» родительский частный метод (т. дочерний класс)

Текст в круглых скобках - это , противоположный тексту перед ним. Java позволяет «независимо реализовать [частный метод] с той же сигнатурой в дочернем классе». Если этого не допустить, это нарушит инкапсуляцию, как я объяснил выше.

Но «запретить« переопределить »родительский частный метод» - это нечто иное и необходимо для обеспечения инкапсуляции .

27
ответ дан 24 November 2019 в 18:50
поделиться
Другие вопросы по тегам:

Похожие вопросы: