Creating circular generic references

2019-01-11 14:14发布

问题:

I am writing an application to do some distributed calculations in a peer to peer network. In defining the network I have two class the P2PNetwork and P2PClient. I want these to be generic and so have the definitions of:

P2PNetwork<T extends P2PClient<? extends P2PNetwork<T>>>

P2PClient<T extends P2PNetwork<? extends T>>

with P2PClient defining a method of setNetwork(T network). What I am hoping to describe with this code is:

  1. A P2PNetwork is constituted of clients of a certain type
  2. A P2PClient may only belong to a network whose clients consist of the same type as this client (the circular-reference)

This seems correct to me but if I try to create a non-generic version such as

MyP2PClient<MyP2PNetwork<? extends MyP2PClient>> myClient;

and other variants I receive numerous errors from the compiler. So my questions are as follows:

  1. Is a generic circular reference even possible (I have never seen anything explicitly forbidding it)?
  2. Is the above generic definition a correct definition of such a circular relationship?
  3. If it is valid, is it the "correct" way to describe such a relationship (i.e. is there another valid definition, which is stylistically preferred)?
  4. How would I properly define a non-generic instance of a Client and Server given the proper generic definition?

回答1:

Circular generic references are indeed possible. Java Generics and Collections includes several examples. For your case, such a specimen would look like this:

public interface P2PNetwork<N extends P2PNetwork<N, C>,
                            C extends P2PClient<N, C>> {
  void addClient(C client);
}

public interface P2PClient<N extends P2PNetwork<N, C>,
                            C extends P2PClient<N, C>> {
  void setNetwork(N network);
}

class TorrentNetwork implements P2PNetwork<TorrentNetwork, TorrentClient> {
  @Override
  public void addClient(TorrentClient client) {
    ...
  }
}

class TorrentClient implements P2PClient<TorrentNetwork, TorrentClient> {
  @Override
  public void setNetwork(TorrentNetwork network) {
    ...
  }
}

...

TorrentNetwork network = new TorrentNetwork();
TorrentClient client = new TorrentClient();

network.addClient(client);


回答2:

It might help us to answer you if you further defined what "a certain type" means, i.e. what the differences are between various "types" of P2PNetworks.

But instead of expressing the dependency / circular relationship in terms of each other, it might be easier to express by introducing a third class, the P2PType:

public class P2PNetwork<T extends P2PType> {
    ...
}
public class P2PClient<T extends P2PType> {
    ...
    public void setNetwork(P2PNetwork<T> network) { ... }
}

I might be overlooking something but I think this would allow the compiler to enforce that P2PClients are a part of P2PNetworks of the same generic type.

This approach might fall apart however if the "type" isn't something that is suitable to express as a object-oriented itself, i.e. if the P2PType isn't something that would have methods, polymorphic behavior, etc.