我的问题非常复杂且难以解释,因此我建立了一个较小的示例。它仍然很复杂,但让我尽力而为...
您可以在此处下载完整的示例:https://mega.co.nz/#!400lSbqa!NoyflWYk6uaQToVDEwXyn22Bdcn_6GdTxB6dPUfU5FU

我建议将其导入到您喜欢的IDE中并在其中玩耍。



假设您正在编写多人游戏。它应该拥有一个包含实体的世界。该代码应分为服务器,客户端和共享内容。

我的每个实体均包含3个文件:


基础,其中包含共享的代码和资源,例如名称
客户端,它派生基础并包含渲染等。
服务器端,它也派生基础并包含网络事件等。


因为我只能从一个类派生,但也希望我的客户/服务器实体也有一些共享的代码,所以我尝试使用委托样式的结构。让我们将实际实体命名为[default],用*标记接口,并用extends / implements标记<-

- *BaseEntity*
- [DefaultBaseEntity] <- *BaseEntity*

- *ClientEntity* <- *BaseEntity*
- [DefaultClientEntity] <- [DefaultBaseEntity], *ClientEntity*

- *ServerEntity* <- *BaseEntity*
- [DefaultServerEntity] <- [DefaultBaseEntity], *ServerEntity*


这样,我还可以通过鸭式输入访问服务器/客户端特定的实现以及仅持有ClientEntity / ServerEntity的基本实现。

现在,我要对包含这些实体的世界进行编程。世界范围的代码也应分为三部分,并且应通用以包含服务器或客户端实体。

package base;

import java.util.ArrayList;
import java.util.List;

public abstract class BaseWorld<E extends BaseEntity> {

    private List<E> entities;

    public BaseWorld() {
        entities = new ArrayList<>();
    }

    public void addEntity(E entity) {
        entity.setWorld(this);
        entities.add(entity);
    }

    public List<E> getEntities() {
        return entities;
    }

    public void doStuffWithBuilding(E entity) {
        entity.doBasestuff();
    }

}




package client;

import base.BaseWorld;

public class ClientWorld extends BaseWorld<ClientEntity>{

}




package server;

import base.BaseWorld;

public class ServerWorld extends BaseWorld<ServerEntity> {

}




如您所见,我为我的实体提供了对其所处世界的反向引用。这包含了实际的问题。

看一下相应的实体代码:

package base;

public class DefaultBaseEntity implements BaseEntity {

    private BaseWorld world;

    @Override
    public void doBasestuff() {
        System.out.println("I am base entity");
    }

    @Override
    public void setWorld(BaseWorld world) {
        this.world = world;
    }

    @Override
    public BaseWorld getWorld() {
        return world;
    }

}


现在可以使用,但是BaseWorld是原始类型。显然,每个IDE都开始抱怨。我也不想抑制警告。

我也不能使用BaseWorld<? extends BaseEntity>这样的通配符类型,因为当我调用doStuffWithBuilding()这样的世界方法时,它们会产生编译错误:

package client;

import base.DefaultBaseEntity;

public class DefaultClientEntity extends DefaultBaseEntity implements ClientEntity {

    @Override
    public void doClientstuff() {
        System.out.println("I am client");
        getWorld().doStuffWithBuilding(this);
    }

}



  方法doStuffWithBuilding(?capture#1-of?extended BaseEntity)在
  BaseWorld类型不是
  适用于参数(DefaultClientEntity)


有什么解决办法吗?我尝试从基本接口中删除set / getWorld()并将其添加到客户端和服务器中,但这非常笨拙,并且由于委派而导致很多麻烦。

最佳答案

您可以通过参数化DefaultBaseEntity来解决此问题:

public class DefaultBaseEntity <E extends BaseEntity>
        implements BaseEntity<E> {

    private BaseWorld<E> world;

    // ...
}

public class DefaultClientEntity extends DefaultBaseEntity<ClientEntity>
        implements ClientEntity {
    // ...
}


请注意,即使DefaultClientEntity的超类是必需的,也不必对其进行参数化(至少不需要为此设置参数)。

更新:
此外,您可以使用接口执行类似的参数化:

interface BaseEntity <E extends BaseEntity> {
    public void setWorld(BaseWorld<E> world);
    // ...
}

interface ClientEntity extends BaseEntity<ClientEntity> {
    // ...
}


上面的示例DefaultBaseEntity代码已更新为实现该通用BaseEntity接口。

09-06 17:20