[英]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#contains
和List#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) ){...
其中alreadyPassedThru
是HashSet<ChatClient>
和knownClient
是ChatClient
與此代碼:
// 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
的規范使我希望如此。
這是行不通的,因為equals
和hashCode
是直接在存根上計算的,無論您在服務器上實現了什么。
這給您兩個選擇:
(我建議)依靠默認值等於。 客戶端ID應該是唯一的,並且不應有兩個具有相同ID的客戶端。 在您的實現中顯然不是這種情況,因為您使用的是額外的id
字段,這意味着實例的身份是不夠的。 我認為應該是。
將您的ChatClient
包裹在CustomHashChatClient
並使用Set<CustomHashChatClient>
,其中CustomHashChatClient
根據對象名稱,id或所需的任何CustomHashChatClient
計算自定義哈希(當然,您需要吞下異常或使異常失敗,使得它不是一個好主意)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.