简体   繁体   English

如何获得等于和哈希码以与Java RMI一起使用?

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

I'm trying to learn Java RMI and I need to compare some objects. 我正在尝试学习Java RMI,并且需要比较一些对象。 Specifically, I want to know if a set already contains an element, but it doesn't detect that an object is already present in the set. 具体来说,我想知道一个集合是否已经包含一个元素,但是它不能检测到该集合中已经存在对象。 I don't have any other communication problems with RMI in general. 总的来说,我与RMI没有其他通信问题。

I've tried overriding hashcode() and equals(Object obj) in my remote object implementation, which is what I have to do from my understanding. 我已经尝试在我的远程对象实现中重写hashcode()equals(Object obj) ,这正是我的理解。

Here's some code: 这是一些代码:

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#contains and List#contains just don't work. Set#containsList#contains不起作用。 Is there some other trick to this? 还有其他技巧吗?

Some more complete code: 一些更完整的代码:

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();
                    }
                }
            }

It's worth mentioning that if I replace the code if ( alreadyPassedThru.contains(knownClient) ) (which is where the problem is) with some code that iterates over the set alreadyPassedThru and manually checks if any of the elements match knownClient by comparing their respective clientName variables, then everything works fine. 值得一提的是,如果我替换码if ( alreadyPassedThru.contains(knownClient) )它是问题所在)与一些代码,通过所述一组迭代alreadyPassedThru和手动检查是否有任何的元素的匹配knownClient通过比较其各自的clientName变量,那么一切正常。

The MessageImpl code: 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]";
        }
    }
}

Just to clarify the difference between when the application works and when it doesn't work: if I replace this: 只是为了阐明应用程序何时可以工作与什么时候不能工作之间的区别:如果我替换了这个:

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

where alreadyPassedThru is a HashSet<ChatClient> and knownClient is a ChatClient with this code: 其中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;
   }
}

...then it works. ...然后工作。

You should try using instanceof instead of the getClass() method. 您应该尝试使用instanceof而不是getClass()方法。

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

This is, at the very least, more efficient and may be the cause of your issue. 至少,这效率更高,并且可能是造成问题的原因。

You might also want to consider simplifying your logic a bit. 您可能还需要考虑简化逻辑。 Right now, you have 3 return points in your code and the intent is not immediately clear. 现在,您的代码中有3个返回点,并且意图尚不明确。 Try this: 尝试这个:

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

They already work with Java RMI. 他们已经在使用Java RMI。

Remote objects and their stubs have the same hashcodes and equals() behaviour by default if the remote objects extend UnicastRemoteObject. 如果远程对象扩展UnicastRemoteObject. ,则默认情况下,远程对象及其存根具有相同的哈希码和equals()行为UnicastRemoteObject.

However you've broken that by overriding hashCode() and equals(). 但是,通过覆盖hashCode()equals().打破了这一点equals().

Just remove those overrides. 只需删除这些替代。

Try doing as follows: 尝试执行以下操作:

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();
    } 
}

I am definitely not sure this will work, but the specification for RemoteObject.equals makes me hope so. 我绝对不确定这是否行得通,但是RemoteObject.equals的规范使我希望如此。

EDIT 编辑

This can't work, because equals and hashCode are computed directly on the stub, regardless of what you implemented on your server. 这是行不通的,因为equalshashCode是直接在存根上计算的,无论您在服务器上实现了什么。

This leaves you with two options: 这给您两个选择:

  1. (which I suggest) rely on the default equals. (我建议)依靠默认值等于。 The client ID should be unique, and there should be no two clients with the same ID. 客户端ID应该是唯一的,并且不应有两个具有相同ID的客户端。 That's clearly not the case in your implementation, because you're using an additional id field, implying the identity of an instance is not enough. 在您的实现中显然不是这种情况,因为您使用的是额外的id字段,这意味着实例的身份是不够的。 I think it should be. 我认为应该是。

  2. Wrap your ChatClient s in a CustomHashChatClient and use a Set<CustomHashChatClient> , with CustomHashChatClient computing a custom hash, based on the object name, id, or whatever you want (of course, you need to swallow the exceptions or fail on them, which makes this a not-so-good idea). 将您的ChatClient包裹在CustomHashChatClient并使用Set<CustomHashChatClient> ,其中CustomHashChatClient根据对象名称,id或所需的任何CustomHashChatClient计算自定义哈希(当然,您需要吞下异常或使异常失败,使得它不是一个好主意)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM