Java, Невозможно уменьшить видимость унаследованного метода от объекта

Продолжая этот вопрос: Почему вы не можете уменьшить видимость метода в подклассе Java?

Мне нужно создать класс B, который почти идентичен классу A, за исключением того, что B не может выполнять определенные действия, которые A может.

Будучи ленивым программистом, как я, я пытался наследовать A, только чтобы приветствовать ошибку, которая B не может уменьшить видимость методов A. Duh!..

Теперь A является API от поставщика, я намерен инкапсулировать этот API, чтобы его было проще использовать.

Интересно, что лучше всего подходит для этого?

Ответы

Ответ 1

Два варианта:

Если вам нужен B, чтобы поддерживать тот же интерфейс, что и A (чтобы клиентский код мог использовать любой из двух без изменений), вы можете переопределить "запрещенные" методы в B и заставить их бросить UnsupportedOperationException. Например:

public class A
{
    public int allowedMethod() { ... }
    public int forbiddenMethod() { ... }
}

public class B extends A
{
    public int forbiddenMethod()
    {
        throw new UnsupportedOperationException("Sorry, not allowed.");
    }
}

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

    public class A
    {
        public int allowedMethod() { ... }
        public int forbiddenMethod() { ... }
    }

    public class B
    {
        private A a;

        public int allowedMethod()
        {
            return a.allowedMethod();
        }
    }

Ответ 3

Фасад используется, когда требуется более простой или простой интерфейс для работы.

Вам нужно будет создать свой собственный класс-оболочку (шаблон фасада) вокруг вашего внешнего интерфейса.

interface Foreign
{
    void dontWantThis();
    void keepThis();
}

interface/class MyForeign
{
    void keepThis();
}

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

Ответ 4

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

Ответ 5

Если B не может делать все, что может сделать A, вы не можете рассматривать B как A.

Может быть, вам нужна обертка, а не подкласс.

EDIT:

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

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

Ответ 6

Решение, вероятно, будет использовать "состав" над "свойством". У вас может быть свойство в классе B типа A. Затем выведите только те методы из B, которые вы хотите реально реализовать

Ответ 7

Другой вариант, который вы можете рассмотреть.

Предположим, вы хотите уменьшить API некоторого класса:

public class LargeApi {
  public void doFoo() { ... }
  public void doBar() { ... }
  public void doBaz() { ... }
  ...
  ...
}

Таким образом, клиенты будут подвержены действию только метода doFoo (или любого метода, который вы предпочитаете использовать):

public interface ReducedApi {
  void doFoo();
}

Но для того, чтобы экземпляры ReducedApi использовались везде, где ожидается BigApi, вам нужен способ вернуться к LargeApi (желательно без кастинга):

public interface ReducedApi {
  void doFoo();
  LargeApiClass asLargeApiClass();
}

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

public class ReducedApiImpl 
  extends LargeApi 
  implements ReducedApi {

  // Don't let them instantiate directly, force them to use the factory method
  private ReducedApiImpl() {

  }

  // Notice, we're returning the ReducedApi interface
  public static ReducedApi newInstance() {
    return new ReducedApiImpl();
  }

  @Override
  public void doFoo() {
    super.doFoo();
  }

  @Override
  public LargeApi asLargeApi() {
    return this;
  }
}

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

ReducedApi instance = ReducedApiImpl.newInstance();
instance.doFoo();
callSomeMethodExpectingLargeApi(instance.asLargeApi());