Reputation: 2355
To begin with I am not sure how to express the issue but I am really stuck (I read this Why can I still make changes to this final variable? but it didn't help). I use CodenameOne framework but I am not sure too the issue spurts from it, it could be general java.
In my App I am sending Reports to the server. It works great.
The method is as follows in the Report class:
public void sendAsync(SuccessCallback<Report> onSuccess) {
// Update : With a shallow copy it does not work
// final Report currentReport = this;
// But with a deeper copy it does
final Report currentReport = clone(this);
final MultipartRequest request = new MultipartRequest() {
@Override
protected void readResponse(InputStream input) throws IOException {
// ... Does stuff here on Report's attributes
// Here the variable currentReport changes during overlapping sendings
@Override
postResponse(){
// The goal is to use some attributes from the current report
// to be shown in a popup to remind the user what we sent
// BUT the as written above the currentReport changes during
// overlapping sendings so that what is shown to the user only
// deals with the lastest report and does not make sense.
onSuccess(currentReport);
}
}
} // end of sendAsync
I call it like this when the user presses the send button:
Report myReport = new Report();
myReport.photoPath.set(...);
myReport.id.set(...);
anotherMethod(myReport); // this passes myReport to sendAsync but never modifies it
// Here myReport has still the right attributes based on the parameters
myReport.sendAsync((sentReport) -> {/* does something else */});
The problem is that when the user presses the send button whereas a previous sending is going on, the previous sending takes the attributes of the following one and it is NOT what I want.
So I don't know where to search in my code, the variables are not static. How can I make previous currentReport not change when the sendings overlap ?
February 25th 2018's update
What I don't understand is why I have to create a deep copy of this
to make it work (the static Report clone(Report sourceReport)
method creates a new Report, sets its attributes with the ones of the passed in sourceReport, and returns the created Report) ?
Moreover when the user presses the button the following is done :
Report report = new Report();
report.photoPath.set(the_path_returned_from_the_camera);
report.otherAttributes.set(values);
report.sendAsync((sentReport) -> {
showConfirmationPopup(sentReport);
// However here report.photoPath returns a different path
// from sentReport.photoPath which surprises me. Why do they differ ?
System.out.println(report.photoPath.get()); //prints /home/a.jpg
System.out.println(sentReport.photoPath.get()); //prints /home/b.jpg
});
What I don't understand too is why within the lambda, report and sendReport do not have the same content when a deep copy is done (there were the same with a shallow copy) ?
The Report constructor works like :
public Report() {
this.location.set(null);
this.photoPath.set("");
...
this.connectionError = false;
}
Thank you very much in advance, I can provide more details if requested
Upvotes: 2
Views: 110
Reputation: 52770
Every invocation of the method creates a new stack frame and places the variable within hence you have a "new final" object. Both are unmodifiable but both are new.
From reading your description I think you are looking at the wrong thing. I suggest placing a breakpoint on the point in which you invoke addToQueue and also look at the details in the network monitor to gain insight into what is happening in the code. My guess is that you are getting two requests one after the other and your server only accepts the last one due to application state.
Upvotes: 1
Reputation: 643
In Java. there is no built-in way to identify which method modify an object or not and thus no way to block those (ex: no equivalent to C++ "const methods"). However, there are design patterns to achieve what you are trying to do, depending on the specifics.
Immutable: simply make an object without setters so it cannot be modified after creation; the reference can still be changed (unless final) however.
Memento (a variant of at least): basically take a snapshot/copy of the object when the event is fired so any modifications being done to the original object after the event is fired doesn't impact the ones from event already fired. Be careful tho, you might need some kind of "deep-clone", if your object contains any other object, doint obj1 = obj2
will only make the two reference the same instance which can still be modified.
Another option around your issue could simply be to prevent event re-submission until the previous event finished (successfully or with Exception/failure.
Upvotes: 1