How to declare a variable that contains a subclass of a class that implements an interface?

I want to declare a variable that contains a class that implements a specific interface. In particular, I am trying to keep SocketChannelit DatagramChannelin the same property so that I can use them interchangeably. Both of these classes extend SelectableChannelas well as implement ByteChannel, and I want to call methods from both. I do not want to store this in two separate variables, because I have to be the same object. I want to pass this object to a single variable in the constructor.

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

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

Note:

I am not looking for other ways to deal with networks that avoid this problem or other ways to implement networks. This question is how to declare a variable to hold a subclass of a class that also implements a specific interface. I just gave the specifics so that you know that I cannot create a new interface or subclass, because in this case all the classes involved are part of the package java.nio.

+3
source share
5 answers

In Java, there is no way to declare a variable the way you would like to make it.

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) - , ).

+3

, . :

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

, factory , :

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

, , . , , :

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;
  }
}
+1

Java , . , , Java.

, :

  • , ,
  • .

, . Typecasts , JIT- .

(, TypeParameter TypeArgument Java, .)

+1

?

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

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

Unfortunately, calling this function using a non-SocketChannel-non-DatagramChannel SelectableChannel would be an exception at runtime instead of a compile-time error. But you can make it private and have two public methods, one of which accepts a SocketChannel and one that accepts a DatagramChannel. Even if your interface is more complex, you can configure the customizers in the same 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);
}
0
source

Source: https://habr.com/ru/post/1770524/


All Articles