New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug on @Getter(lazy=true) and transient fields #1236

Closed
manuel-hegner opened this Issue Nov 17, 2016 · 5 comments

Comments

Projects
None yet
5 participants
@manuel-hegner

Hey,
there is a problem that occurs if you have a transient field that is laziely initialized. The generated getter method crashes after deserialization because the field is null. The expected behaviour would be to call the lazy creationmethod again.

So instead of generating something like:

public double[] getCached() {
	java.lang.Object value = this.cached.get();
	if (value == null) {
		synchronized(this.cached) {
			value = this.cached.get();
			if (value == null) {
				final double[] actualValue = expensive();
				value = actualValue == null ? this.cached : actualValue;
				this.cached.set(value);
			}
		}
	}
	return (double[])(value == this.cached ? null : value);
} 

It should be something like this, at least for transient values:

public double[] getCached() {
	if(cached==null) {
		synchronized(this) {
			if(cached==null)
				cached = new java.util.concurrent.AtomicReference<java.lang.Object>();
		}
	}
	java.lang.Object value = this.cached.get();
	if (value == null) {
		synchronized(this.cached) {
			value = this.cached.get();
			if (value == null) {
				final double[] actualValue = expensive();
				value = actualValue == null ? this.cached : actualValue;
				this.cached.set(value);
			}
		}
	}
	return (double[])(value == this.cached ? null : value);
} 
@bulgakovalexander

This comment has been minimized.

Show comment
Hide comment
@bulgakovalexander

bulgakovalexander Nov 21, 2016

Contributor

cached = new java.util.concurrent.AtomicReference<java.lang.Object>();
It does not compile because the field is final.

Contributor

bulgakovalexander commented Nov 21, 2016

cached = new java.util.concurrent.AtomicReference<java.lang.Object>();
It does not compile because the field is final.

@manuel-hegner

This comment has been minimized.

Show comment
Hide comment
@manuel-hegner

manuel-hegner Nov 21, 2016

Oh yeah. I forgot that the field is final. So to prevent the serialization bug you could either make it not final or you have to create a totally different way to serialize/deserialize this.

Another way would be to add an error if a lazy getter field is transient.

Oh yeah. I forgot that the field is final. So to prevent the serialization bug you could either make it not final or you have to create a totally different way to serialize/deserialize this.

Another way would be to add an error if a lazy getter field is transient.

@Maaartinus

This comment has been minimized.

Show comment
Hide comment
@Maaartinus

Maaartinus Nov 21, 2016

Contributor

I'm afraid the field must be final in order to be visible in other threads (I guess, it could be volatile instead).

Serialization is a dirty beast and AFAIK uses some kind of reflection for setting final fields. This is probably what also lombok should do for transient fields. I believe that there are no visibility issues (as reflection emits the memory barrier needed).

A compile-time error is IMHO a good step before a better solution gets implemented.

Contributor

Maaartinus commented Nov 21, 2016

I'm afraid the field must be final in order to be visible in other threads (I guess, it could be volatile instead).

Serialization is a dirty beast and AFAIK uses some kind of reflection for setting final fields. This is probably what also lombok should do for transient fields. I believe that there are no visibility issues (as reflection emits the memory barrier needed).

A compile-time error is IMHO a good step before a better solution gets implemented.

@rspilker rspilker closed this in 8ce09de Dec 5, 2016

@rspilker

This comment has been minimized.

Show comment
Hide comment
@rspilker

rspilker Dec 5, 2016

Collaborator

Using reflection in a readResolve method that also should be generated if it's not already there, or added to an existing readResolve is for us way too much effort for use case that we didn't encounter a lot. So we chose to give a compile time error instead.

Collaborator

rspilker commented Dec 5, 2016

Using reflection in a readResolve method that also should be generated if it's not already there, or added to an existing readResolve is for us way too much effort for use case that we didn't encounter a lot. So we chose to give a compile time error instead.

teosarca added a commit to metasfresh/metasfresh that referenced this issue Apr 8, 2017

@deinspanjer

This comment has been minimized.

Show comment
Hide comment
@deinspanjer

deinspanjer May 24, 2017

Oy. I just tried upgrading my Lombok dependency to 1.16.16 and hit this error.
I have several fields which need to be lazily initialized, but they need to be transient because I don't want those fields to be read by Gson when converting the object to JSON.

Any tips on a way around this other than downgrading?

Oy. I just tried upgrading my Lombok dependency to 1.16.16 and hit this error.
I have several fields which need to be lazily initialized, but they need to be transient because I don't want those fields to be read by Gson when converting the object to JSON.

Any tips on a way around this other than downgrading?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment