【问题标题】:Generic method in interface returns a non-checked cast接口中的通用方法返回未经检查的强制转换
【发布时间】:2017-03-22 23:38:59
【问题描述】:

所以我有一系列相关的接口,其中一个包含其他 3 个(由 setter 和 getter 强制执行)。

虽然此接口的实例可以在管理(从地图中存储和检索)时保留为接口合同;当他们到达一个处理 bean 时,他们需要能够将他们自己显示为他们所实现的类。

所以我能够做到以下几点:

public interface IState<S extends IState> {
<G extends GameData> G getGameData();
<G> S setGameData(G gameDataIO);

<P> P getPlayer();
<P> S setPlayer(P player);

<GS> GS getGameState();
<GS> S setGameState(GS gameState);}

默认实现:

public class DefaultStateNG implements IStateNG<DefaultStateNG> {

private GameData gameDataIO;
private IPlayerState player;
private IGameState gameState;


@Override
public <G extends GameData> G getGameData() {
    return (G) gameDataIO;
}

@Override
public <G> DefaultStateNG setGameData(G gameDataIO) {
    this.gameDataIO = (GameData) gameDataIO;
    return this;
} //..... other accessor methods implemented

因此,当我在处理器 bean 中使用它们时,不需要将它们强制转换为接口的适当实现。

当在返回的类型中强制转换时抛出未经检查的强制转换,就会出现问题。我不知道如何处理这样的警告。我的意思是我将 gameDataIO 设置为 Game 数据类型,并且返回应该是 GameData 类型。为什么警告?我尝试了各种类型的转换,到目前为止唯一没有警告的情况是将预期的类作为参数传递,然后使用 type.cast(returnedObject)。

任何提示将不胜感激。

【问题讨论】:

  • 我认为您不想在这里使用泛型。如果您想使用来自不同树的类而不使用它们的组合父级(最坏情况下的对象),则使用泛型。在这里,你只想拥有 GameData、Player 等。
  • GameData、Player等是Spring启动时注入实现的接口。所以我有接口只是为了确保基本功能,但实现的类需要处理器知道。我做了第一个没有泛型的版本,但最后我做了大量的类型转换,使代码不可读。

标签: java spring generics interface casting


【解决方案1】:

当你的接口定义这样的泛型方法时:

<G extends GameData> G getGameData();

编译器知道&lt;G&gt; 必须扩展或实现GameData,但G 所代表的确切实现将在调用的代码位置推断 em> getGameData() 方法。

当你实现那个方法时:

@Override
public <G extends GameData> G getGameData() {
    return (G) gameDataIO;
}

您在此处的实现对任何潜在的调用站点一无所知。它应该返回调用者 (G) 所需的 特定 子类型,而它不能这样做。您正在强制转换,但编译器无法验证它是否安全(因为它不是),因此您会收到 unchecked 警告。

假装写了一个类MyGameData extends GameData并把它写在我的代码中:

IState<?> state = ... // TODO
MyGameData mgd = state.getGameData();

在这种情况下,&lt;G&gt; 绑定到 特定 类型 MyGameData,这符合您的 API 要求,即它必须继承自 GameData。但随后您的代码返回一个普通的 GameData not 我的特定子类,并在运行时导致 ClassCastException。

您的大多数其他方法也是如此。要么返回基本类型(GameData 等)而不制作 getter 等泛型方法或者您必须添加类型参数 GGSPIState 接口,而不是基于每个方法。


编辑添加,基于此评论:

GameData、Player 等是 Spring 在启动时注入其实现的接口。所以我有接口只是为了确保基本功能,但实现的类需要处理器知道。我做了第一个没有泛型的版本,但最后我做了大量的类型转换,导致代码不可读。

您正在描述紧耦合系统的症状。 Interfaces are a tool 用于定义松散耦合系统,因为这些通常被认为是更好的设计。我鼓励您考虑为什么在您的设计中处理器需要如此了解其他接口的实现类型。是因为接口本身并没有完全描述该接口的实现所需的功能行为吗?

【讨论】:

  • 我明白你的意思,但是在我的处理器 bean 实现中,将输入设置为 IState 的实现,因此传入参数是 DefaultState state;然后我可以做MyGameData mgd = state.getGameData();没有什么问题。我让我的代码正常工作,正是这个警告让我感到困扰。从你所说的我明白为什么我会收到警告,并且我不能确保类型安全,所以我应该使用抑制警告标签?
  • 我不建议取消此警告(或一般警告),因为这意味着事情会中断,除非情况恰好正确的。这是很多空洞的挥手,可能会给你以后带来麻烦。 我的建议是您将 API 更改为不对这些方法使用泛型,因为它们会导致您的 API 建议它支持您的实现无法支持的东西。
  • 确实,我的目标是拥有一个非常松耦合的系统。我知道我在做什么听起来违反直觉,但处理器需要知道实现的原因是处理器本身是另一个注入接口。所以我正在做的是一个通用游戏服务器,其中状态存储在不同的商店中。软件的每个部分都应该能够互换(配置)。所以 State/Player Manager 并不关心实现的细节,但游戏本身会关心,而且游戏使用了在界面中可能不可见的特定字段。
  • 一个游戏和它自己的状态应该是紧密耦合的,但是我不希望它们被注入/管理为单独的bean。从通用状态管理器检索保存的特定于游戏的状态实现将在某处需要强制转换;由于擦除,这不是泛型可以自行解决的问题。
【解决方案2】:

我想出了一个解决方案,即在已实现处理器的入口点上定义类型。我无法找到使用泛型方法设置类型声明的方法(至少不需要进行未经检查的强制转换)。

William 之后,我很清楚必须进行声明,并且我的处理器 bean 是所有其他 bean 连接在一起的地方,因此声明类型是有道理的。不幸的是,状态是我的处理方法的 I/O 对象,因此它还需要在处理器中声明,包括它包含的类。

类似:

@Component
public class KenoManager implements IGenericGame<DefaultState<
    KenoData,
    DefaultPlayer,
    KenoGameState,
    DefaultSystemState>> {

public DefaultState processInit(DefaultState<
        KenoData,
        DefaultPlayer,
        KenoGameState,
        DefaultSystemState> state) {
    return null;
}

当配置文件出现时,我可以使用通配符吗?填写声明并使某些 bean 的注入成为通用的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-09
    • 1970-01-01
    • 2013-02-07
    • 1970-01-01
    • 1970-01-01
    • 2018-10-10
    • 1970-01-01
    相关资源
    最近更新 更多