避免 inheritance/delegation 结构中的原始泛型类型

avoid raw generic type in inheritance/delegation structure

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

我建议将其导入到您最喜欢的 IDE 中并进行尝试。


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

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

因为我只能派生自一个 class 但希望我的 client/server 实体也有一些共享代码,所以我尝试使用委托式结构。让我们将实际实体命名为 [default],用 * 标记接口,用 <-

标记接口 extends / implements
- *BaseEntity*
- [DefaultBaseEntity] <- *BaseEntity*

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

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

这样,我也可以 duck-typing-access server/client 特定实现加上仅包含 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);
    }

}

The method doStuffWithBuilding(capture#1-of ? extends BaseEntity) in the type BaseWorld is not applicable for the arguments (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 接口。

您必须在所有地方放置正确的类型参数:

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

public interface ClientEntity<E extends BaseEntity> extends BaseEntity<E> { ... }

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

    private BaseWorld<E> world;

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

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

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

}

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