Защищенные абстрактные методы, не видимые в экземпляре экземпляра дочернего класса родителя

Я пытаюсь создать BufferedServerSocketImpl который возвратит BufferedInputStream в getInputStream вместо небуферизованного одного из обернутого объекта (markSupported()= false), но в противном случае просто переадресует все остальные вызовы на обернутый объект SocketImpl.

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

public class BufferedServerSocketImpl extends SocketImpl { private SocketImpl internalSocketImpl; BufferedServerSocketImpl(SocketImpl nested) { this.internalSocketImpl = nested; } @Override protected void accept(SocketImpl s) throws IOException { internalSocketImpl.accept(s); } //...
}

В приведенном выше примере internalSocketImpl.accept(s); является ошибкой, потому что метод accept(SocketImpl) из типа SocketImpl не отображается.

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

Любые идеи о том, почему это так и как я могу обойти это?

Я рассматривал использование динамических прокси для Socket или SocketImpl, но поскольку у них нет интерфейсов, мне придется либо использовать внешнюю библиотеку, либо делать манипуляции с байт-кодом, ни одна из которых не возможна для проекта. Кроме того, я работаю в среде OSGi, поэтому shenanigans класса loadload также не осуществимы.

4 ответа

Если вы не можете найти ни одного интерфейса или абстрактного класса для вашего объекта делегата, который содержит все методы, которые вы хотите делегировать, прямой путь не будет ни расширяться, ни делегировать (даже если вы не должны делать оба) (*).

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

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

Method method = internalSocketImpl.getClass.getMethod("accept", SocketImpl.class);
method.invoke(internalSocketImpl, s);

Он прост в реализации, но имеет последствия, и вы теряете весь контроль времени компиляции доступных методов. Вам придется поймать исключение из getMethod (NoSuchMethodException) и invoke (IllegalAccessException, IllegalArgumentException, InvocationTargetException)

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

ServerSocket server = (ServerSocket) internalSocketImpl;
server.accept(s);

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

Последний вариант можно использовать, если в предыдущем варианте использования все методы можно найти в интерфейсах, это проксирование. Вместо создания нового класса вы принимаете только прокси для своего делегата. Но я не буду подробно останавливаться на этом, потому что метод accept ServerSocket не существует ни в каком методе, реализованном ServerSocket.

Это было для общего случая, но в вашем реальном случае, я думаю, вы должны использовать разные подклассы, по одному для каждого подкласса SocketImpl, который вы хотите расширить, и вместо этого использовать объект буферизации, который вы помещаете во все ваши подклассы в качестве помощника для переопределения методы, которые вам нужны. Это позволило бы избежать шаблона расширения + делегата (*)

(*) Вы расширяете SocketImpl, это означает, что ваш класс имеет экземпляр всех полей SocketImpl и не использует их, потому что вы делегируете всю фактическую обработку другому объекту internalSocketImpl. Это то, что другие подразумевали, говоря, что вы смешиваете наследование и состав.


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

Вместо этого вы должны просто использовать наследование.

public class BufferedServerSocketImpl extends SocketImpl { @Override protected void accept(SocketImpl s) throws IOException { super.accept(s); } //...
}

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

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


Это не очень безопасно, но вы можете попробовать.

В eclipse, если вы настроены на просмотр исходного кода, дважды щелкните SocketImpl, чтобы получить исходный код.

Затем создайте пакет java.net в своем проекте и создайте класс с именем SocketImpl и пакет java.net, используя этот исходный код.

Найдите метод accept(), чтобы изменить его видимость для публики.

Измените его и на BufferedServerSocketImpl.

Внедрите все другие методы, требуемые вашим (новым) абстрактным суперклассом.

Помните: вы обманываете загрузчика классов.Быть осторожен.


вы не можете вызвать internalSocketImpl.accept(s) ;, вы должны использовать super.accept(s);

licensed under cc by-sa 3.0 with attribution.