简体   繁体   English

受保护的抽象方法在父类的子类包装实例中不可见

[英]Protected abstract methods not visible in child class wrapping instance of parent

I'm trying to create a BufferedServerSocketImpl which would return a BufferedInputStream in getInputStream instead of the non-buffered one of the wrapped object ( markSupported() =false), but otherwise simply forward all other calls to the wrapped SocketImpl object. 我正在尝试创建一个BufferedServerSocketImpl ,它将在getInputStream返回BufferedInputStream而不是包装对象的非缓冲对象( markSupported() = false),但是否则将所有其他调用转发到包装的SocketImpl对象。

The issue I'm having is with accept and other protected abstract methods not being visible. 我遇到的问题是accept和其他受保护的抽象方法不可见。 Here's an excerpt: 摘录如下:

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

In the above example internalSocketImpl.accept(s); 在上面的示例中, internalSocketImpl.accept(s); is an error because the method accept(SocketImpl) from the type SocketImpl is not visible. 错误是因为不可见SocketImpl类型的方法accept(SocketImpl)

I know that the object I'll wrap will be a java.net.PlainServerSocketImpl or a java.net.SocksSocketImpl , but I can't extend those as they are not visible either. 我知道我要包装的对象将是java.net.PlainServerSocketImpljava.net.SocksSocketImpl ,但是由于它们也不可见,所以我无法对其进行扩展。

Any ideas on why this is so and how I could work around it? 关于为何如此以及如何解决的任何想法?

I considered using dynamic proxies for Socket or SocketImpl , but since they do not have interfaces I would have to either use an external library or do bytecode manipulations, neither of which are feasible for the project. 我考虑过将动态代理用于SocketSocketImpl ,但是由于它们没有接口,因此我将不得不使用外部库或字节码操作,这两种方法都不适合该项目。 Also, I'm running in an OSGi framework, so classloader shenanigans are also not feasible. 另外,我在OSGi框架中运行,因此classloader的恶作剧也不可行。

You are trying to use both composition and inheritance in your class. 您正在尝试在类中同时使用组合和继承。 This is, in my opinion somewhat misguided. 我认为这有些误导。

What you should instead be doing, is to just use inheritance. 相反,您应该做的是仅使用继承。

public class BufferedServerSocketImpl extends SocketImpl {

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

Alternatively if you really want to use composition you can declare your class to be in the same package. 另外,如果您真的想使用组合,则可以声明您的类在同一包中。 However the fact, that the class is an abstract class in my opinion is an strong indicator that it shouldn't be used for composition. 但是,在我看来,该类是一个抽象类是一个有力的指标,表明不应将其用于合成。

A better approach, for the composition approach will be to walk down the inheritance hierarchy until a suitable candidate class is found. 对于组合方法,更好的方法是沿继承层次结构走,直到找到合适的候选类为止。

If you cannot find a single interface or abstract class for your delegate object that contains all methods you want to delegate, there will be no direct way neither for extending, nor for delegating (even if you should not make both)(*). 如果找不到包含所有要委派方法的委派对象的单个接口或抽象类,则将没有直接方法既不能扩展也不能委派(即使您不应该两者都做)(*)。

I can only imagine 2 options : duck typing through reflection or multiple interfaces usage. 我只能想象2种选择:通过反射或多个接口使用鸭子

In duck typing, you never wonder what class the delegate is nor what interface it implements, you just requires it has a method with a given name and parameter types and you get it with reflection 在鸭子类型中,您永远不会怀疑委托是什么类,或者它实现了什么接口,您只需要它具有一个具有给定名称和参数类型的方法,并且可以通过反射来获取它。

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

It is simple to implement, but has performances implication and you loose all compile time control on accessible methods. 它实现起来很简单,但是对性能有影响,您会在可访问方法上失去所有编译时间控制。 You will have to catch the exception from both getMethod ( NoSuchMethodException ) and invoke ( IllegalAccessException , IllegalArgumentException , InvocationTargetException ) 您将必须从getMethodNoSuchMethodException )和invokeIllegalAccessExceptionIllegalArgumentExceptionInvocationTargetException )中捕获异常。

<TL/DR mode>

In multiple interface/superclasses usage, you pass an object implementing one, and you state in the documentation that it must also implement/extend others. 在多种接口/超类用法中,传递一个实现一个对象的对象,然后在文档中声明该对象还必须实现/扩展其他对象。 Compiler cannot help you in controling that, but nevertheless it should work provided the interface/superclass is implemented/extended : 编译器无法帮助您进行控制,但是只要接口/超类已实现/扩展,它就可以正常工作:

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

It is much cleaner, but you must find a set of interfaces or superclasses, containing all the methods you need and implemented/extended by any of you possible delegates. 它更干净,但是您必须找到一组接口或超类,其中包含您需要的方法以及由任何可能的委托实现/扩展的所有方法。 You will have to test for ClassCastException . 您将必须测试ClassCastException

The last option is usable if in previous use case, all methods can be found in interfaces, it is proxying. 如果在以前的用例中,所有方法都可以在接口中找到,则最后一个选项可用,它是代理。 Instead of creating a new class, you only take a proxy to your delegate. 无需创建新类,您只需将代理代理到您的代理即可。 But I won't elaborate on it any further, because the method accept of ServerSocket does not exist in any method implemented by ServerSocket . 但我不会在其上再进一步详谈,因为该方法acceptServerSocket由实施的任何方法不存在ServerSocket

</ TL/DR mode>

That was for the general case, but in your real case, I think you should use different subclasses, one for each SocketImpl subclass you want to extend, and instead use a buffering object that you put in all your subclasses as a helper for overriding the methods that you need to. 那是一般情况,但在实际情况下,我认为您应该使用不同的子类,对于要扩展的每个SocketImpl子类一个子类,而应使用放入所有子类中的缓冲对象作为覆盖该子类的辅助对象。您需要的方法。 That would avoid the extend + delegate pattern (*) 这样可以避免扩展+委托模式(*)

(*) You are extending SocketImpl , that means that your class has an instance of all fields of SocketImpl, and do not use them because you are delegating all actual processing to another object internalSocketImpl . (*)您正在扩展SocketImpl ,这意味着您的类具有SocketImpl所有字段的实例,并且不使用它们,因为您将所有实际处理委托给另一个对象internalSocketImpl That is what others meant by saying you mix inheritance and composition. 这就是别人说您将继承与构成混为一谈的意思。

you can't call internalSocketImpl.accept(s); 您不能调用internalSocketImpl.accept(s); , you should use super.accept(s); ,则应使用super.accept(s);

This is not very safe but you may try. 这不是很安全,但是您可以尝试。

On eclipse, if you're configured to see the source code , double click on SocketImpl to get its source code. 在eclipse上,如果已配置为查看源代码 ,请双击SocketImpl以获取其源代码。

Then create a package java.net in your project and create a class named SocketImpl and package java.net using this source code. 然后在您的项目中创建一个包java.net,并创建一个名为SocketImpl的类,并使用此源代码来打包java.net。

Find the accept() method, to change its visibility to public. 找到accept()方法,以将其可见性更改为public。

Change it from your BufferedServerSocketImpl as well. 也从您的BufferedServerSocketImpl更改它。

Implement all other methods required by your (new) abstract superclass. 实现您的(新)抽象超类所需的所有其他方法。

Remember: you're cheating the classloader. 记住:您在欺骗类加载器。 Be careful. 小心。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用子类和抽象父类中受保护的成员变量 - The use of protected member variables from child class and abstract parent 如何在Java中抽象父级的子类中初始化受保护的最终变量? - How to initialize a protected final variable in a child class of an abstract parent in Java? 当父级位于不同的程序包中且子类为Abstract时,模拟受保护的父级方法 - Mock Protected Parent Method When the Parent is in a Different Package and the Child Class is Abstract 父包装子类方法 - Parent wrapping child class method 父母班子女的实例 - Instance of child in parent class 子类不可见的抽象类中受保护接口中的方法 - Method in protected interface within an abstract class not visible to a subclass Java:如何从抽象父类创建新的子类实例? - Java: How to create new child class instance from abstract parent class? 访问扩展了抽象基类的类实例的受保护变量? - Accessing Protected Variable Of An Instance Of A Class That Extends An Abstract Base Class? Java抽象类:受保护的字段或公共集/获取方法 - Java abstract class: protected fields or public set/get methods 如何使用 JUnit 和 JMock 测试抽象类的受保护方法 - How to test protected methods of abstract class using JUnit and JMock
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM