如何声明包含实现接口的类的子类的变量?

时间:2010-10-20 12:41:41

标签: java variables interface subclass nio

我想声明一个包含实现特定接口的类的变量。具体来说,我正在尝试将SocketChannelDatagramChannel存储在同一属性中,以便我可以互换使用它们。这两个类都扩展SelectableChannel并实现ByteChannel,我希望从两者中调用方法。我不想将它存储在两个单独的变量中,因为我需要将它们作为同一个对象。我想将这个对象在一个变量中传递给构造函数。

甚至可以这样做吗?如果没有,那么仍然支持这种模型的常见解决方法是什么?为清楚起见,这里是(不正确的)声明可以描述我正在尝试做什么,如果它们有效:

private SelectableChannel & ByteChannel byteChannel;
private SelectableChannel implements ByteChannel byteChannel;
private ? extends SelectableChannel implements ByteChannel byteChannel;

请注意:

我不是在寻找其他方法来处理避免此问题的网络,或其他实现网络的方法。这个问题是关于声明一个变量来保存一个类的子类,该类也实现了一个特定的接口。我只给出了具体信息,以便您知道我无法创建新的接口或子类,因为在这种情况下,所涉及的所有类都是java.nio包的一部分。

5 个答案:

答案 0 :(得分:3)

Java中没有办法按照您希望的方式声明变量。

您可以使用SelectableChannel作为变量的类型(因为这是SocketChannelDatagramChannel的超类型),并且每当您将其转换为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);
}