2009-12-07 40 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)

相關問題