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();
}
}
Ответ 2
Используйте Состав, а не Наследование.
то есть. класс B содержит ссылку на класс A и внутренне вызывает на нем методы.
Ответ 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());
Ответ 8
Я бы использовал шаблон адаптера. http://en.wikipedia.org/wiki/Adapter_pattern