Можно ли скрыть или снизить доступ к Inherited Methods в Java?

У меня есть структура класса, где я хотел бы, чтобы некоторые методы в базовом классе были доступны из классов, полученных непосредственно из базового класса, но не классов, производных от производных классов. Согласно спецификации языка Java, можно переопределить спецификации доступа на унаследованных методах, чтобы сделать их более открытыми, но не более частными. Например, это суть того, что мне нужно сделать, но является незаконным:

// Defines myMethod
public class Base {
    protected void myMethod() {}
}

// Uses myMethod and then hides it.
public class DerivedOne extends Base {
    @Override
    private void myMethod();
}

// can't access myMethod.
public class DerivedTwo extends DerivedOne {

}

Есть ли способ сделать это?

Отредактировано, чтобы объяснить, почему я хотел бы сделать это:

В этом случае структура класса представляет собой структуру обработки и импорта данных. Он считывает и анализирует текстовые файлы, полные табличных данных, а затем сохраняет их в базе данных.

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

Средний класс относится к типу таблицы в анализируемом файле и имеет логику синтаксического анализа и импорта. Он нуждается в доступе к некоторым функциям доступа к базе данных базового класса.

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

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

Возможно, скрытие - это неправильный способ выразить это: мне действительно нужно сделать некоторые функции, которые должны быть частными для базового класса до уровня первого уровня в иерархии. Скрытие это выполнит, но я вижу, как скрыть проблему. Есть ли другой способ сделать это?

Ответ 1

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

Похоже, вы описываете классическую проблему ORM. Давайте посмотрим на это снова и посмотрим, можем ли мы переквалифицировать его на другие понятия, чем строгий "наследование", потому что я действительно считаю, что ваша проблема не техническая, она концептуальна:

Ты сказал:

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

Это может быть более понятно, но похоже, что у нас есть один класс, который должен управлять соединением DB и обычными операциями db. Следуя Single Responsibility, я думаю, что мы закончили здесь. Вам не нужно расширять этот класс, вам нужно передать его классу, который должен использовать его функциональность.

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

"Средний класс" здесь немного похож на Data Mapper. Этот класс не должен расширять предыдущий класс, он должен владеть ссылкой на него, возможно, встраивается в конструктор или сеттер в качестве интерфейса.

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

Непонятно, почему класс высокого уровня, похоже, знает о схеме db (по крайней мере, то, что мне предлагает фраза "инициализировать расположение таблицы" ), но опять же, если связь между двумя первыми классы были encapsulation ( "имеет" / "использует" ) вместо наследования ( "is a" ), я не думаю, что это быть проблемой.

Ответ 2

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

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

Ответ 3

При переопределении метода вы можете сделать его более общедоступным, не более приватным. Я не знаю, почему вы используете слово "общий"

Помните, что при заказе от минимального к самому ограничительному:

public<protected<default<private

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

Может Вы можете переопределить метод protected с помощью public.

Не удается: Вы не можете переопределить метод public с помощью protected.

Ответ 4

Если вы это сделали, то DerivedOne не будет базой, с точки зрения DerivedTwo. Вместо этого вам нужен класс-оболочка

//Uses myMethod but keeps it hidden
public class HiddenBase {
    private final Base base = new Base();
    private void myMethod();
    public void otherMethod() {base.otherMethod();}
}

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

Ответ 5

Наследование работает, потому что везде, где вы можете использовать базовый класс, вы также можете использовать один из его подклассов. Поведение может быть другим, но API нет. Концепция известна как принцип подстановки Лискова.

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

То, что вы на самом деле хотите выполнить, может быть выполнено с помощью интерфейсов:

interface IBase1 {
}

class Derived1 implements IBase1 {
  public void myMethod() {
  }
}

class Derived2 implements IBase1 {
}

class UseMe {
  public void foo(IBase1 something) {
     // Can take both Derived1 and Derived2
     // Can not call something.myMethod()
  }
  public void foo(Derived1 something) {
     something.myMethod();
  }
  public void foo(Derived2 something) {
    // not something.myMethod()
  }
}

Ответ 6

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

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

// Uses myMethod and then hides it.
public class DerivedOne extends Base {
    @Override
    public void myMethod() {
        throw new IllegalStateException("Illegal access to myMethod");
    }

    private void myPrivateMethod() {
        super.myMethod();
    }

}

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

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

Интерфейс будет реализован в (static?), вложенном внутри среднего класса. Я имею в виду:

public interface Specific {
    public void doSomething();
}

public class Base {
    private final Specific specificImpl;

    protected Base(Specific s) {
        specificImpl = s;
    }

    public void doAlot() {

         // ...

         specificImpl.doSomething();

         // ...
    }
}

public class Middle extends Base {

    public Middle() {
        super(new Impl());
    }

    private Impl implements Specific {

        public void doSomething() {

            System.out.println("something done");
        }
    }
}

public class Derived extends Middle {

    // Access to doAlot()
    // No access to doSomething()
}

Ответ 7

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

рассмотрим следующее:


package a;

public class Base {
    void myMethod() {
        System.out.println("a");
    }
}

package a;

public class DerivedOne extends Base {
    @Override
    void myMethod() {
        System.out.println("b");
    }
}

package b;

public class DerivedTwo extends a.DerivedOne {
    public static void main(String... args) {
        myMethod(); // this does not compile...
    }
}

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

Ответ 8

вы должны сделать метод final, когда переопределите его

public class Base {
protected void myMethod() {}
}

// Uses myMethod and then hides it.
public class DerivedOne extends Base {
@Override
final protected void myMethod(); //make the method final
}


public class DerivedTwo extends DerivedOne {
   // can't access myMethod here.
}