java – How do I declare a variable that contains a subclass of a class which implements an interface? – Education Career Blog

I want to declare a variable which holds a class which implements a specific interface. Specifically, I’m trying to store a SocketChannel and a DatagramChannel in the same property so I can use them interchangeably. Both of these classes extend SelectableChannel and also implement ByteChannel, and I wish to call methods from both. I do not want to store this in two separate variables, because I they need to be the same object. I want to pass this object in one variable to the constructor.

Is it even possible to do this? If not, what are the common workarounds which still support this type of model? For clarity, here are the (incorrect) declarations which could describe what I’m trying to do, were they valid:

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

Please Note:

I am not looking for other ways to handle networking which avoids this problem, or other ways to implement networking. This question is about declaring a variable to hold a subclass of a class which also implements a specific interface. I only gave the specifics so that you would know that I cannot create a new interface or subclass because in this case all of the classes involved are part of the java.nio package.

,

There is no way in Java to declare the variable the way you would like to do it.

You could use SelectableChannel for the type of the variable (since this is a supertype of both SocketChannel and DatagramChannel), and cast it to a ByteChannel whenever you need to call methods from that interface. Simple example:

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

(Or the other way around: declare the variable as a ByteChannel and cast to a SelectableChannel when necessary – whichever is more convenient in your case).

,

Fields can’t be of a disjunction type, you can only use that syntax as part of a type bound in a type parameter list. You could make your class generic:

class Foo<C extends SelectableChannel & ByteChannel> {
    private C selectableByteChannel;
}

That puts a bit of extra strain on the client code, though, so you could hide this stuff behind a factory method and a private implementation class which is generic:

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
        }
    }
}

,

I don’t think there’s a direct way to do what you want. You could define an interface and two proxy classes to expose the methods you want to use:

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 ...
}

or

class GenericWrapper<C extends SelectableChannel & ByteChannel> implements GenericChannel {
  private final C channel;
  public DatagramWrapper(C channel) {
    this.channel = channel;
  }
}

,

The type of a variable in a Java variable declaration must either be a definite type or the name of a type parameter. So what you are trying to express is not expressible in Java.

I’m afraid your only choices are:

  • use two variables with the respective interface types containing the same reference, or
  • use one variable with a common supertype and use typecasts.

The latter is not as bad as all that. Typecasts aren’t that expensive, and the JIT compiler may be able to optimize some or all of them away.

(Incidentally, your “syntax” looks rather like the syntax for declaring a TypeParameter or TypeArgument in Java. But it is not real.)

,

Doesn’t casting take care of this?

public void doWhatever(SelectableChannel foo) {
    // Call a SocketChannelMethod
    ((SocketChannel)foo).someSocketChannelMethod();

    // Call a DatagramChannel method
    ((DatagramChannel)foo).someByteChannelMethod();
}

Unfortunately, calling this function with a non-SocketChannel-non-DatagramChannel SelectableChannel would be a runtime exception instead of a compile-time error. But you could make it private and have two public methods, one that accepts a SocketChannel and one that accepts a DatagramChannel. Even if your interface is more complex than that, you could have your setters setup in a similar way:

public void setChannel(SelectableChannel foo) {
    this.channel = foo;    
}
public void setChannel(SocketChannel foo) {
    setChannel((SelectableChannel) foo);
}
public void setChannel(DatagramChannel foo) {
    setChannel((SelectableChannel) foo);
}

Leave a Comment