是否可以在Java中阻止/拒绝强制转换?

时间:2009-12-07 22:50:15

标签: java casting

我有一个简单游戏的代码,其中必须实现AgentInterface才能为游戏中的一个角色创建代理控制器。 GameState是一个实现GameStateInterface的类,实现此接口的对象可以传递给代理,因此代理可以从游戏状态读取和分析数据,并且代理必须返回相应的操作(作为int返回)角色应该采取。

这是代理必须实现的AgentInterface:

public interface AgentInterface {
    // the return value specifies the direction of the joystick
    public int action(GameStateInterface gs);
}

使用名为MyAgent的代理运行游戏:

    GameState gs = new GameState();
    AgentInterface agent = new MyAgent();
    while (true) {
        // more code here
        int bestAction = agent.action(gs)
        // more code here
    }        

但是,GameState中有一些信息代理不应该访问,因为这会欺骗控制器。但是,执行从GameStateInterface到GameState的强制转换将允许代理访问GameStateInterface中未定义的信息,如下所示:

public MyAgent implements AgentInterface {
    public int action(GameStateInterface gs) {
        int nLives = ((GameState) gs).nLivesRemaining; // IS IT POSSIBLE TO DENY/PREVENT THIS CAST??
        // Do more stuff here
        return BestAction;
    }
}

我的问题是,是否可以阻止转换?我知道多态性是Java和面向对象编程语言的主要特性之一,但在这种情况下我想避免转换。

我知道这可以通过许多其他方式解决,但我很想知道是否可以这样做。

提前致谢。

10 个答案:

答案 0 :(得分:12)

据我所知,拦截类型转换并拒绝它(例如,通过抛出ClassCastException)是不可能的。

但是,您可以简单地使用Proxy pattern来控制对实际GameState对象的访问,而不是试图拒绝类型转换。只需实现一个代理类,它只实现GameStateInterface并让它将所有方法调用转发给GameState对象。现在,不是将实际的GameState对象引用传递给action方法,而是将它传递给代理类的实例包装。

答案 1 :(得分:4)

无法阻止演员阵容。但是,您可以定义游戏状态,使其只能从特定位置构建。我想到的一件事是实现接口的私有内部类,或者返回私有内部类实例的工厂

答案 2 :(得分:4)

答案很简单“不要在你的代理代码中强制转换为GameState”。

或者,您可以将GameState内容声明为私有。或者,如果您需要从其他几个类中访问它,请将其声明为受包保护。

答案 3 :(得分:4)

通常,您无法阻止在Java中强制转换对象。接收对GameState的引用的代码将能够在该对象上调用任何非私有的,未受保护的方法。即使你可以防止施法,它仍然可以使用反射。

如果Agent代码在您的控制之下,请保持简单并且不要施放。如果其他人编写Agent类,您可以创建一个代理类,它接受GameState对象并仅实现GameStateInterface的方法。

class GameStateProxy implements GameStateInterface {
    private GameStateInterface state;

    public GameStateProxy(GameState state) {
        this.state = state;
    }

    public int someMethodInGameStateInterface(int x) {
        return state.someMethodInGameStateInterface(x);
    }

    // other methods ...
}

然后你可以创建一个代理并传递它:

GameStateInterface proxy = new GameStateProxy(gameState);
int bestAction = agent.action(proxy);

收到GameStateProxy的代码只能访问GameStateInterface中的方法。

答案 4 :(得分:2)

如果您担心代理更改游戏状态,则创建状态的bean副本并将其传递给代理,而不是真正的GameState对象。

禁止播放听起来不可能(它可能是一个不可阻止的JVM语言规范功能),或者我从未听说过它。

答案 5 :(得分:2)

我正在实施一个安全的只读对象。如果您创建只读接口(无设置器),您仍然可以进行类型转换和访问纯对象的方法。例如,Interface只有一个get,并且该Interface的子节点具有该集合。如果将对象强制转换为接口,则只有get。但是你仍然可以对这个对象进行类型转换并访问所有内容:(

为避免这种情况,您可以创建仅由该类的创建者拥有的复合。这是一个例子:

public class ItemReadOnly {

private String m_name;

private ItemReadOnly(String name){
    m_name = name;
}

public String getName(){
    return m_name;
}

private void setName(String name){
    m_name = name;
}

public static Item createItem(String name){
    return new Item(new ItemReadOnly(name));
}

private static class Item {

    private ItemReadOnly m_readOnlyInstance;

    public Item(ItemReadOnly readOnlyInstance){
        m_readOnlyInstance = readOnlyInstance;
    }

    public void setName(String name){
        m_readOnlyInstance.setName(name);
    }

    public String getName(){
        return m_readOnlyInstance.getName();
    }

    public ItemReadOnly getReadOnlyInstance(){
        return m_readOnlyInstance;
    }
}
}

这样,您输入:

Item item = ItemReadOnly.createItem(name); 

所以他可以访问Item对象(内部类可以访问私有方法:))然后,如果你想只读访问这个项目:

ItemReadOnly readOnly = item.getReadOnlyInstance();

现在,绝对不可能进行类型转换,因为它们根本不属于同一类型!

希望这可以帮助别人! (我想如果你提到来源:P)

答案 6 :(得分:1)

我们做的是给出一个带有“Stubs”的jar,你可以编译它,但它不包含任何实现。当实际产品运行时,我们用真正的罐子替换存根。

但在我们的案例中,我们控制它的运行位置。

在我们的案例中,我们也完全按照您的要求行事。任何类都必须请求访问其他类(在运行时)。我相信这是所有自定义实现,但我不确定它是否可以在任何JVM上运行。

你可以尝试查找/请求/我正在处理的东西的源代码。如果您说您有兴趣开发可能能够获得它的电缆盒,那么可以使用参考实现。它被称为“tru2way”或“OCAP”参考堆栈实现,我认为该项目可以在某个地方的Java站点上获得。可能需要一些谷歌搜索 - 我相当肯定你会发现它都是在一个特殊的类加载器或SecurityManager中完成的。

编辑:我想我可能错了。我们所做的是根据所访问类的名称,使用安全管理器创建“权限”。当一个线程试图在类上调用一个方法时,我们首先测试它的权限(我们在“protected”类中编写代码),如果当前线程没有通过类名识别的权限,它会抛出一个异常。

与您相同的效果,但更慢,更冗长。但是我们必须阻止孩子们观看。

编辑2 :(抱歉!!)

查看这样的权限说明让我相信它必须至少部分可能:

这授予代码查询类的公共,受保护,默认(包)访问以及私有字段和/或方法的权限。虽然代码可以访问私有和受保护的字段和方法名称,但它不能访问私有/受保护的字段数据,也无法调用任何私有方法。然而,恶意代码可能会使用此信息来更好地针对攻击。此外,它可以调用任何公共方法和/或访问类中的公共字段。如果代码通常无法调用这些方法和/或访问字段,这可能会很危险,因为它无法使用这些方法和字段将对象强制转换为类/接口。

否则,如何防止applet实例化和访问任意JVM类? “Dangerous”路径可能会被阻止,就像我们阻止我们的东西一样 - 通过每次调用它们来读取检查权限 - 但上面的引用似乎有更多可用且大多数类完全被阻止默认值。

这让我感兴趣了一段时间,但我从未真正研究过它。

答案 7 :(得分:1)

只能转换为可访问类型。通过将GameState设为私有,受包保护或受保护,您可以限制谁可以投射它。

如果您运行的是不受信任的代码,请务必安装安全管理器,因为可能会使用反射来绕过其失效中的访问修饰符(c.f。Field.setAccessible

答案 8 :(得分:0)

不,没有办法做到这一点。

祝你好运,
费边

答案 9 :(得分:0)

我不知道你在Java中描述的内容是否可行。在其他语言中,您可以重载类型转换操作符并让它们抛出异常或其他东西,但这在Java中是不可能的。你最好的选择可能就是你所谈到的“许多其他方式”之一。