google / google/gson

Why not lazy initialization boundFields in ReflectiveTypeAdapterFactory.Adapter?

Open
#2,143 1 comment 0 reactions 0 assignees View on GitHub
enhancement
Dominant language
Java
Stars
24.2k
Forks
4.5k
Avg merge
6d 4h
Merged PRs (30d)
12

Description

In the latest code which commit is `cbc0af867b898d9e8244f268f4ffe37dfcaf8ea7`, `ReflectiveTypeAdapterFactory.Adapter` needs `boundFields` in its construct and the `getBoundFields` method will collect all fileds. But in some case, maybe we don't need collect all boundFileds to serialize or deserialize. In below example, the json string just contains `userFiled`, thus create bound filed with `Article` is redundant and will increate cost time.
Example:
```Java
// Complex Model
public class ComplexModel {
private User userFiled;
private Article article;
private String stringFiled;

private static class User {
private String aFiled;
private String bFile;
}

private static class Article {
private User userFiled;
private String aFiled;
private String bFile;
}
}
```

```Json
{
"userFiled": {
"aFiled": "",
"bFiled": ""
}
}
```

I think the better way is to create boundfiled on deman. Maybe lazy initialization can improve performance, especially for mobile platforms such as Android. I didn't read the entire code, is there any other consideration for not doing this?

```Java
@Override
public TypeAdapter create(Gson gson, final TypeToken type) {
Class raw = type.getRawType();

if (!Object.class.isAssignableFrom(raw)) {
return null; // it's a primitive!
}

ReflectionAccessFilter.FilterResult filterResult = ReflectionAccessFilterHelper.getFilterResult(reflectionFilters, raw);
if (filterResult == ReflectionAccessFilter.FilterResult.BLOCK_ALL) {
throw new JsonIOException("ReflectionAccessFilter does not permit using reflection for "
+ raw + ". Register a TypeAdapter for this type or adjust the access filter.");
}
boolean blockInaccessible = filterResult == ReflectionAccessFilter.FilterResult.BLOCK_INACCESSIBLE;

ObjectConstructor constructor = constructorConstructor.get(type);
return new LazyReflectiveTypeAdapterFactory.Adapter<>(constructor, new LazyInitBoundFiledCallback() {
@Override
public Map call() {
return getBoundFields(gson, type, raw, blockInaccessible);
}
});
}

public static final class Adapter extends TypeAdapter {
private final ObjectConstructor constructor;
private final LazyInitBoundFiledCallback lazyInitBoundFiledCallback;
private volatile Map boundFields;

Adapter(ObjectConstructor constructor, LazyInitBoundFiledCallback lazyInitBoundFiledCallback) {
this.constructor = constructor;
this.lazyInitBoundFiledCallback = lazyInitBoundFiledCallback;
}

private void ensure() {
if (boundFields == null) {
synchronized (this) {
if (boundFields == null) {
boundFields = lazyInitBoundFiledCallback.call();
}
}
}
}

@Override
public T read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}

T instance = constructor.construct();

ensure();
try {
in.beginObject();
while (in.hasNext()) {
String name = in.nextName();
BoundField field = boundFields.get(name);
if (field == null || !field.deserialized) {
in.skipValue();
} else {
field.read(in, instance);
}
}
} catch (IllegalStateException e) {
throw new JsonSyntaxException(e);
} catch (IllegalAccessException e) {
throw ReflectionHelper.createExceptionForUnexpectedIllegalAccess(e);
}
in.endObject();
return instance;
}

@Override
public void write(JsonWriter out, T value) throws IOException {
if (value == null) {
out.nullValue();
return;
}

ensure();
out.beginObject();
try {
for (BoundField boundField : boundFields.values()) {
boundField.write(out, value);
}
} catch (IllegalAccessException e) {
throw ReflectionHelper.createExceptionForUnexpectedIllegalAccess(e);
}
out.endObject();
}
}

interface LazyInitBoundFiledCallback {
Map call();
}
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.