Можно ли скрыть или уменьшить доступ к унаследованным методам в Java? - PullRequest
13 голосов
/ 14 декабря 2009

У меня есть структура классов, где я хотел бы, чтобы некоторые методы в базовом классе были доступны из классов, производных непосредственно от базового класса, но не из классов, производных от производных классов. В соответствии со спецификацией языка 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 {

}

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

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

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

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

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

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

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

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

Ответы [ 8 ]

17 голосов
/ 15 декабря 2009

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

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

Вы сказали:

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

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

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

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

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

Мне не ясно, почему высокоуровневый класс, кажется, знает схему БД (по крайней мере, это то, что мне предлагает фраза «инициализация макета таблицы»), но опять же, если связь между первыми двумя классы были инкапсуляцией («имеет» / «использует») вместо наследования («является») , я не думаю, что это было бы проблемой.

10 голосов
/ 14 декабря 2009

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

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

6 голосов
/ 14 декабря 2009

Если бы вы сделали это, 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();}
}

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

6 голосов
/ 14 декабря 2009

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

Помните, что порядок от наименьшего до самого ограничительного:

public<protected<default<private

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

Can: Вы можете переопределить метод protected на public.

Не могу: Вы не можете переопределить метод public методом protected.

3 голосов
/ 14 декабря 2009

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

учтите следующее:


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...
    }
}

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

3 голосов
/ 14 декабря 2009

Наследование работает, потому что везде, где вы можете использовать базовый класс, вы также можете использовать один из его подклассов. Поведение может отличаться, но 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()
  }
}
2 голосов
/ 31 декабря 2015

вы должны сделать метод 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.
}
2 голосов
/ 14 декабря 2009

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

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

// 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();
    }

}

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

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

Интерфейс будет реализован в виде (статического?) Вложенного в средний класс. Что я имею в виду выглядит так:

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()
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...