我想声明一个包含实现特定接口的类的变量。具体来说,我正在尝试将SocketChannel
和DatagramChannel
存储在同一属性中,以便我可以互换使用它们。这两个类都扩展SelectableChannel
并实现ByteChannel
,我希望从两者中调用方法。我不想将它存储在两个单独的变量中,因为我需要将它们作为同一个对象。我想将这个对象在一个变量中传递给构造函数。
甚至可以这样做吗?如果没有,那么仍然支持这种模型的常见解决方法是什么?为清楚起见,这里是(不正确的)声明可以描述我正在尝试做什么,如果它们有效:
private SelectableChannel & ByteChannel byteChannel; private SelectableChannel implements ByteChannel byteChannel; private ? extends SelectableChannel implements ByteChannel byteChannel;
请注意:
我不是在寻找其他方法来处理避免此问题的网络,或其他实现网络的方法。这个问题是关于声明一个变量来保存一个类的子类,该类也实现了一个特定的接口。我只给出了具体信息,以便您知道我无法创建新的接口或子类,因为在这种情况下,所涉及的所有类都是java.nio
包的一部分。
答案 0 :(得分:3)
Java中没有办法按照您希望的方式声明变量。
您可以使用SelectableChannel
作为变量的类型(因为这是SocketChannel
和DatagramChannel
的超类型),并且每当您将其转换为ByteChannel
需要从该接口调用方法。简单的例子:
class MyClass {
private SelectableChannel channel; // either a SocketChannel or a DatagramChannel
public int readStuff(ByteBuffer buffer) {
// Cast it to a ByteChannel when necessary
return ((ByteChannel) channel).read(buffer);
}
}
(或者反过来说:将变量声明为ByteChannel
并在必要时强制转换为SelectableChannel
- 以您的情况更方便为准。)
答案 1 :(得分:2)
字段不能是析取类型,您只能将该语法用作类型参数列表中绑定类型的一部分。你可以让你的班级通用:
class Foo<C extends SelectableChannel & ByteChannel> {
private C selectableByteChannel;
}
但是,这会对客户端代码造成一些额外的压力,所以你可以将这些东西隐藏在工厂方法和通用的私有实现类之后:
abstract class Foo {
private Foo(){}
public abstract void doSomethingWithASelectableByteChannel();
public static <C extends SelectableChannel & ByteChannel>
Foo createFoo(C channel) {
return new FooImpl<C>(channel);
}
private static final class FooImpl<C extends SelectableChannel & ByteChannel>
extends Foo
{
private final C selectableByteChannel;
private FooImpl(C channel){
selectableByteChannel = channel;
}
public void doSomethingWithASelectableByteChannel(){
// Do stuff with your selectableByteChannel
}
}
}
答案 2 :(得分:1)
我认为没有直接的方法来做你想做的事。您可以定义一个接口和两个代理类来公开您要使用的方法:
interface GenericChannel {
// ... methods you want to use ...
}
class SocketWrapper implements GenericChannel {
private final SocketChannel channel;
public SocketWrapper(SocketChannel channel) {
this.channel = channel;
}
// ... pass through calls to this.channel ...
}
class DatagramWrapper implements GenericChannel {
private final DatagramChannel channel;
public DatagramWrapper(DatagramChannel channel) {
this.channel = channel;
}
// ... pass through calls to this.channel ...
}
或
class GenericWrapper<C extends SelectableChannel & ByteChannel> implements GenericChannel {
private final C channel;
public DatagramWrapper(C channel) {
this.channel = channel;
}
}
答案 3 :(得分:1)
Java变量声明中的变量类型必须是明确类型或类型参数的名称。所以你想表达的东西在Java中是不可表达的。
我担心你唯一的选择是:
后者没有那么糟糕。类型转换并不昂贵,JIT编译器可能能够优化其中的部分或全部。
(顺便说一下,你的“语法”看起来很像在Java中声明TypeParameter或TypeArgument的语法。但它并不真实。)
答案 4 :(得分:0)
不投射照顾吗?
public void doWhatever(SelectableChannel foo) {
// Call a SocketChannelMethod
((SocketChannel)foo).someSocketChannelMethod();
// Call a DatagramChannel method
((DatagramChannel)foo).someByteChannelMethod();
}
不幸的是,使用非SocketChannel-non-DatagramChannel SelectableChannel调用此函数将是运行时异常,而不是编译时错误。但是你可以将它设为私有,并有两个公共方法,一个接受SocketChannel,另一个接受DatagramChannel。即使您的界面比这更复杂,您也可以以类似的方式设置您的设置器:
public void setChannel(SelectableChannel foo) {
this.channel = foo;
}
public void setChannel(SocketChannel foo) {
setChannel((SelectableChannel) foo);
}
public void setChannel(DatagramChannel foo) {
setChannel((SelectableChannel) foo);
}