我有一个简单游戏的代码,其中必须实现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和面向对象编程语言的主要特性之一,但在这种情况下我想避免转换。
我知道这可以通过许多其他方式解决,但我很想知道是否可以这样做。
提前致谢。
答案 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中是不可能的。你最好的选择可能就是你所谈到的“许多其他方式”之一。