Skip to content

Why not lazy initialization boundFields in ReflectiveTypeAdapterFactory.Adapter? #2143

@ZhaoxiZhang

Description

@ZhaoxiZhang

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:

// 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;
    }
}
{
    "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?

@Override
public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
    Class<? super T> 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<T> constructor = constructorConstructor.get(type);
    return new LazyReflectiveTypeAdapterFactory.Adapter<>(constructor, new LazyInitBoundFiledCallback() {
        @Override
        public Map<String, BoundField> call() {
            return getBoundFields(gson, type, raw, blockInaccessible);
        }
    });
}

public static final class Adapter<T> extends TypeAdapter<T> {
    private final ObjectConstructor<T> constructor;
    private final LazyInitBoundFiledCallback lazyInitBoundFiledCallback;
    private volatile Map<String, BoundField> boundFields;

    Adapter(ObjectConstructor<T> 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<String, BoundField> call();
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions