簡體   English   中英

如何獲得等於和哈希碼以與Java RMI一起使用?

[英]How to get equals and hashcode to work with Java RMI?

我正在嘗試學習Java RMI,並且需要比較一些對象。 具體來說,我想知道一個集合是否已經包含一個元素,但是它不能檢測到該集合中已經存在對象。 總的來說,我與RMI沒有其他通信問題。

我已經嘗試在我的遠程對象實現中重寫hashcode()equals(Object obj) ,這正是我的理解。

這是一些代碼:

public class ChatClientImpl extends UnicastRemoteObject implements ChatClient {

    private static final long serialVersionUID = 3056117484716596895L;
    private String id = null;

    // some other stuff...

    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (obj == null || !(obj instanceof ChatClientImpl)) {
            return false;
        }
        ChatClientImpl other = (ChatClientImpl) obj;
        return this.id.equals(other.id);
    }

    @Override
    public int hashCode() {
        return this.id.hashCode();
    } 
}

Set#containsList#contains不起作用。 還有其他技巧嗎?

一些更完整的代碼:

public void processMessage(MessageImpl inMessage) throws java.rmi.RemoteException {
        // check the message to see if we have processed it before - discard if so
        if( this.processedMessages.contains(inMessage.id) ) {
            System.err.println("Ignoring duplicate message: " + inMessage.id);
            return;
        }
        else {
            System.out.println("Processing message: " + inMessage.id);
            this.processedMessages.add(inMessage.id);
            System.out.println(this.processedMessages.size() + " messages have been processed");
        }
        // update the GUI display with the new message
        this.gui.updateDisplay(inMessage);

        // add this client to the set of clients that have seen the message
        inMessage.addRecipient(this);

        // pass the message on to one of the clients that haven't seen it yet
        sendMessage(inMessage);
    }

        private void sendMessage(MessageImpl msg) {
                Iterator<ChatClient> clientIterator;
                try {
                    // list of all known clients fetched from a server via RMI call
                    clientIterator = server.getClients().iterator();
                } catch (RemoteException e) {
                    // log the error...
                    return;
                }
                // clients that have already seen the message
                Set<ChatClient> alreadyPassedThru = msg.getRecipients(); 
                boolean messageSent = false;

                while ( ! messageSent && clientIterator.hasNext() ){
                    ChatClient knownClient = clientIterator.next();
                    try {
                        // clients that are in alreadyPassedThru are NOT detected...
                        if ( alreadyPassedThru.contains(knownClient) ){  
                            System.out.println("Skipping client that has already seen the message: " + knownClient.getName());
                        } else {
                            knownClient.processMessage(msg);
                            System.out.println("Message has been sent to " + knownClient.getName());
                            messageSent = true;
                        }
                    } catch (RemoteException e) {
                        // client couldn't be contacted
                        clientIterator.remove();
                    }
                }
            }

值得一提的是,如果我替換碼if ( alreadyPassedThru.contains(knownClient) )它是問題所在)與一些代碼,通過所述一組迭代alreadyPassedThru和手動檢查是否有任何的元素的匹配knownClient通過比較其各自的clientName變量,那么一切正常。

MessageImpl代碼:

public class MessageImpl implements java.io.Serializable, Message {

    private static final long serialVersionUID = 8914588083609635659L;

    /** 
     * The globally unique identifier for this message 
     */
    public final GUID id;

    /** 
     * All remote clients that have seen this message so far
     */
    public final Set<ChatClient> passedThrough = new HashSet<ChatClient>();

    /**
     * The content of the message. 
     */
    private String messageContent = null;

    /**
     * The client who created the object
     */
    private ChatClient author = null; 

    /** 
     * Create a new Message instance. 
     */
    public MessageImpl(ChatClient auth, String msg) {
        this.id = new GUID(auth);
        this.author = auth; 
        this.messageContent = msg; 
        addRecipient(auth); // the author has seen the message
    }

    @Override
    public void addRecipient(ChatClient client) {
        this.passedThrough.add(client);
    }

    @Override
    public Set<ChatClient> getRecipients() {
        return this.passedThrough;
    }

    @Override
    public String getContent() {
        return this.messageContent; 
    }

    public String getSource() {
        try {
            return this.author.getName();   
        } catch (Exception e) {
            return "[Unknown User]";
        }
    }
}

只是為了闡明應用程序何時可以工作與什么時候不能工作之間的區別:如果我替換了這個:

if ( alreadyPassedThru.contains(knownClient) ){...

其中alreadyPassedThruHashSet<ChatClient>knownClientChatClient與此代碼:

// RMI call on ChatClient - simply returns clientName field
String knownClientName = knownClient.getName();
for (ChatClient client : alreadyPassedThru) {
   if ( client.getName().equals(knownClientName) ){
       return true;
   }
}

...然后工作。

您應該嘗試使用instanceof而不是getClass()方法。

if (obj == null || !(obj instanceof MyRemote)) {
    ...
}

至少,這效率更高,並且可能是造成問題的原因。

您可能還需要考慮簡化邏輯。 現在,您的代碼中有3個返回點,並且意圖尚不明確。 嘗試這個:

public boolean equals(Object obj) {
    if (obj instanceof MyRemoteImpl) {
        MyRemoteImpl other = (MyRemoteImpl) obj;
        return this.id.equals(other.id);
    }
    return false;
}

他們已經在使用Java RMI。

如果遠程對象擴展UnicastRemoteObject. ,則默認情況下,遠程對象及其存根具有相同的哈希碼和equals()行為UnicastRemoteObject.

但是,通過覆蓋hashCode()equals().打破了這一點equals().

只需刪除這些替代。

嘗試執行以下操作:

public class ChatClientImpl extends UnicastRemoteObject implements ChatClient {

    private static final long serialVersionUID = 3056117484716596895L;
    private String id = null;

    // some other stuff...

    @Override
    public boolean equals(Object obj) {
        if (super.equals(obj)) {
            //Should also return true for stubs pointing to this
            return true;
        }
        if (obj == null || !(obj instanceof ChatClient)) { //Check against the interface
            return false;
        }
        //Only use the interface. obj might be a stub, not only an impl.
        ChatClient other = (ChatClient) obj;
        return getName().equals(obj.getName());
    }

    @Override
    public int hashCode() {
        return getName().hashCode();
    } 
}

我絕對不確定這是否行得通,但是RemoteObject.equals的規范使我希望如此。

編輯

這是行不通的,因為equalshashCode是直接在存根上計算的,無論您在服務器上實現了什么。

這給您兩個選擇:

  1. (我建議)依靠默認值等於。 客戶端ID應該是唯一的,並且不應有兩個具有相同ID的客戶端。 在您的實現中顯然不是這種情況,因為您使用的是額外的id字段,這意味着實例的身份是不夠的。 我認為應該是。

  2. 將您的ChatClient包裹在CustomHashChatClient並使用Set<CustomHashChatClient> ,其中CustomHashChatClient根據對象名稱,id或所需的任何CustomHashChatClient計算自定義哈希(當然,您需要吞下異常或使異常失敗,使得它不是一個好主意)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM