2009-12-07 70 views
8

我有一个简单的游戏代码,其中AgentInterface必须实现以便为游戏中的其中一个角色创建代理控制器。 GameState是实现GameStateInterface的类,并且实现此接口的对象可以传递给代理,因此代理可以从游戏状态读取和分析数据,并且代理必须返回适当的操作(作为int返回)角色应该采取。是否可以阻止/拒绝Java中的强制转换?

这是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 
    }   

但是,有在游戏状态的一些信息,代理不应该能够访问,因为这将是对控制器的欺骗。但是,这样做从GameStateInterface铸造转化为游戏状态将允许代理访问未在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和面向对象编程语言的主要特性之一,但在这种情况下,我想避免强制转换。

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

在此先感谢。

回答

12

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

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

+0

这似乎是一个很好的解决方案。 +1 – 2009-12-07 22:59:00

+4

警告 - 确保配置了Java安全性,以便代理(即不可信)代码不能使用Reflection API。有人可以使用反射来访问代理类的私有成员来获取GameState对象。 – 2009-12-08 03:14:10

0

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

最良好的祝愿,
费边

0

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

4

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

4

答案很简单:“不要在您的代理程序中投射到GameState中”。

或者,你可以声明GameState的东西是私人的。或者,如果您需要从其他几个选择的类中访问它,请将其声明为受封装保护。

2

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

禁止播放声音听起来不太可能(它可能是不可阻挡的JVM语言规范功能),或者我从来没有听说过。

+0

是的,事实上,一个不可变的bean副本也是我的第一个可能的解决方案。我只想知道其他方法。 +1 – 2009-12-07 23:03:08

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访问方法的代码。

+3

但记得设置一个SecurityManager来禁止反射。没有那么难获得私人'国家'... – 2009-12-07 23:11:08

1

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

但后来在我们的例子中,我们控制在何处运行。

在我们的案例,同时,我们也做你问什么。任何类都必须请求访问其他类(在运行时)。我相信这都是自定义实现,但我不确定它会在任何JVM上运行。

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

编辑:我想我可能是错的。我们所做的是根据所访问类的名称与安全管理器创建“权限”。当一个线程试图调用这个类的一个方法时,我们先测试它的权限(我们把代码写在“protected”类中),并且如果当前线程没有由类的名字标识的权限,它会抛出一个例外。

与您之后的效果相同,但速度较慢且较为冗长。但是,我们必须防止孩子们看着孩子。

编辑2:(对不起!)

看着权限的描述,如这让我相信它必须至少部分可能:

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

否则,如何防止applet实例化和访问任意JVM类? “危险”路径可能与我们拦截我们的东西的方式一样被阻止 - 通过每次调用时都读取检查权限 - 但是上面的引用使得看起来有更多可用并且大多数类完全被阻止默认。

这让我感兴趣了一段时间,但我从来没有真正看过它。

1

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

如果您正在运行不受信任的代码,一定要安装安全管理器,如反射可用于规避访问修饰符在其absensce(C.F. Field.setAccessible

2

我正在执行一项安全只读对象。如果你创建一个只读接口(没有setter),你仍然可以对纯对象进行类型转换和访问。例如,接口只有一个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); 

所以他有项目对象的访问(内部类可以访问私有方法:)),那么如果你想给只读此文件的访问:

ItemReadOnly readOnly = item.getReadOnlyInstance(); 

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

希望这可以帮助别人! (我会喜欢,如果你提到的来源:P)

相关问题