避免 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> { ... }
我的问题非常复杂且难以解释,所以我构建了一个较小的示例。它仍然很复杂,但让我尽力而为... 您可以在此处下载完整示例: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> { ... }