Share
Embed
Transcript
43 slides
-
Problems public class User implements Parcelable { public static final
Creator<User> CREATOR = new Creator<User>() { @Override public User createFromParcel(Parcel in) { return new User(in); } @Override public User[] newArray(int size) { return new User[size]; } }; @SerializedName("username") private String userName; @SerializedName("email") private String email; public User(String userName) { this.userName = userName; } protected User(Parcel in) { userName = in.readString(); email = in.readString(); } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(userName); dest.writeString(email); } @Override public int describeContents() { return 0; } public String getUserName() { return userName; } public String setUserName(String userName) { this.userName = userName; } public String getEmail() { return email; } public String setEmail(String email) { this.email = email; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; if (userName != null ? !userName.equals(user.userName) : user.userName != null) return false; return email != null ? !email.equals(user.email) : user.email != null; } @Override public int hashCode() { int result = 31; result = 31 * result + (userName != null ? userName.hashCode() : 0); result = 31 * result + (email != null ? email.hashCode() : 0); return result; } @Override public String toString() { return "User{userName='" + userName + ", email='" + email; } -
What we write and get @AutoValue public abstract class User
{ @Nullable public abstract String username(); public abstract String email(); } final class AutoValue_User extends User { private final String username; private final String email; AutoValue_User(@Nullable String username, String email) { this.username = username; if (email == null) { throw new NullPointerException("Null email"); } this.email = email; } @Nullable @Override public String username() { return username; } @Override public String email() { return email; } //toString, equals, hashCode… -
How does it works? @Retention(RetentionPolicy.SOURCE) @Target({ ElementType.TYPE}) public @interface AutoValue
{ @Retention(RetentionPolicy.SOURCE) @Target({ElementType.TYPE}) public @interface Builder { } } -
How we instantiate @AutoValue public abstract class User { @Nullable
public abstract String username(); public abstract String email(); public static Builder builder() { return new AutoValue_User.Builder(); } @AutoValue.Builder public abstract static class Builder { public abstract Builder email(String email); public abstract Builder username(String username); public abstract User build(); } } static final class Builder extends User.Builder { private String email; private String username; Builder() { } Builder(User source) { this.email = source.email(); this.username = source.username(); } @Override public User.Builder email(String email) { this.email = email; return this; } @Override public User.Builder username(@Nullable String username) { this.username = username; return this; } @Override public User build() { String missing = ""; if (email == null) { missing += " email"; } if (!missing.isEmpty()) { throw new IllegalStateException("Missing properties:" + missing); } return new AutoValue_User(this.email, this.username); } } -
How we instantiate @AutoValue public abstract class User { @Nullable
public abstract String username(); public abstract String email(); public static User create(String username, String email) { return builder().username(username).email(email).build(); } public static Builder builder() { return new AutoValue_User.Builder(); } @AutoValue.Builder public abstract static class Builder { public abstract Builder email(String email); public abstract Builder username(String username); public abstract User build(); } } -
How we instantiate public class ApiClientMock implements ApiClientType { @Override
public Observable<User> login(LoginBody body) { return Observable.just( User.create("ivan", "[email protected]") ); } } -
Don't use constructors, use builders under the hood @AutoValue public
abstract class User { @Nullable public abstract String username(); public abstract String email(); public static User create(String username, String email) { return new AutoValue_User(username, email); } } -
Don't. Avoid mutable property types @AutoValue public abstract class MutableExample
{ public static MutableExample create(MutablePropertyType ouch) { // Replace `.clone` below with the right copying code for this type return new AutoValue_MutableExample(ouch.clone()); } /** * Returns the ouch associated with this object; <b>do not mutate</b> the * returned object. */ public abstract MutablePropertyType ouch(); } -
Parcelable final class AutoValue_User extends $AutoValue_User { public static final
Parcelable.Creator<AutoValue_User> CREATOR = new Parcelable.Creator<AutoValue_User>() { @Override public AutoValue_User createFromParcel(Parcel in) { return new AutoValue_User( in.readInt() == 0 ? in.readString() : null, in.readString() ); } @Override public AutoValue_User[] newArray(int size) { return new AutoValue_User[size]; } }; AutoValue_User(String username, String email) { super(username, email); } @Override public void writeToParcel(Parcel dest, int flags) { if (username() == null) { dest.writeInt(1); } else { dest.writeInt(0); dest.writeString(username()); } dest.writeString(email()); } @Override public int describeContents() { return 0; } -
How this works public abstract class AutoValueExtension { //Determines whether
this Extension applies to the given context. public boolean applicable(Context context) { return false; } //Denotes that the class generated by this Extension must be the final class in the inheritance hierarchy. public boolean mustBeFinal(Context context) { return false; } //Returns a possibly empty set of property names that this Extension intends to implement public Set<String> consumeProperties(Context context) { return Collections.emptySet(); } //Returns a possible empty set of abstract methods that this Extension intends to implement public Set<ExecutableElement> consumeMethods(Context context) { return Collections.emptySet(); } //Returns the generated source code of the class public abstract String generateClass(Context context, String className, String classToExtend, boolean isFinal); } -
How this works @Override public boolean applicable(Context context) { TypeMirror
autoValueClass = context.autoValueClass().asType(); // Disallow manual implementation of the CREATOR instance ... // Disallow manual implementation of writeToParcel ExecutableElement writeToParcel = findWriteToParcel(context); if (writeToParcel != null) { context.processingEnvironment().getMessager().printMessage(Diagnostic.Kind.ERROR, "Manual implementation of Parcelable#writeToParcel(Parcel,int) found when processing " + autoValueClass.toString() + ". Remove this so auto-value-parcel can automatically generate the " + "implementation for you.", writeToParcel); } TypeMirror parcelable = context.processingEnvironment().getElementUtils() .getTypeElement("android.os.Parcelable").asType(); return TypeSimplifier.isClassOfType(context.processingEnvironment().getTypeUtils(), parcelable, autoValueClass); } -
How this works @Override public Set<String> consumeProperties(Context context) { ImmutableSet.Builder<String>
properties = new ImmutableSet.Builder<>(); for (String property : context.properties().keySet()) { switch (property) { case "describeContents": properties.add(property); break; } } return properties.build(); } -
How this works @Override public Set<ExecutableElement> consumeMethods(Context context) { ImmutableSet.Builder<ExecutableElement>
methods = new ImmutableSet.Builder<>(); for (ExecutableElement element : context.abstractMethods()) { switch (element.getSimpleName().toString()) { case "writeToParcel": methods.add(element); break; } } return methods.build(); } -
How this works @Override public String generateClass(Context context, String className,
String classToExtend, boolean isFinal) { … //using JavaPoet to generate class on top of Spec } MethodSpec generateDescribeContents() { return MethodSpec.methodBuilder("describeContents") .addAnnotation(Override.class) .addModifiers(PUBLIC) .returns(int.class) .addStatement("return 0") .build(); } -
Gson @AutoValue public abstract class User implements Parcelable { @Nullable
public abstract String username(); public abstract String email(); public static TypeAdapter<User> typeAdapter(Gson gson) { return new AutoValue_User.GsonTypeAdapter(gson); } } -
Gson @GsonTypeAdapterFactory public abstract class AdapterFactory implements TypeAdapterFactory { public
static AdapterFactory create() { return new AutoValueGson_AdapterFactory(); } } @Provides @Singleton Gson provideGson() { return new GsonBuilder() .registerTypeAdapterFactory(AdapterFactory.create()) .create(); } -
Gson abstract class $AutoValue_User extends $$AutoValue_User { ... public static
final class GsonTypeAdapter extends TypeAdapter<User> { ... @Override public void write(JsonWriter jsonWriter, User object) throws IOException { if (object == null) { jsonWriter.nullValue(); return; } jsonWriter.beginObject(); jsonWriter.name("username"); usernameAdapter.write(jsonWriter, object.username()); jsonWriter.name("email"); emailAdapter.write(jsonWriter, object.email()); jsonWriter.endObject(); } } @Override public User read(JsonReader jsonReader) throws IOException { if (jsonReader.peek() == JsonToken.NULL) { jsonReader.nextNull(); return null; } jsonReader.beginObject(); String email = this.defaultEmail; String username = this.defaultUsername; while (jsonReader.hasNext()) { String _name = jsonReader.nextName(); if (jsonReader.peek() == JsonToken.NULL) { jsonReader.nextNull(); continue; } switch (_name) { case "username": { username = usernameAdapter.read(jsonReader); break; } case "email": { email = emailAdapter.read(jsonReader); break; -
All Extensions • JSON(Gson/Jackson) • Parcelable • Cursor • Firebase
• Redact(Omitting selected fields from toString()) -
• DRY • Immutable(Type-safe, null-safe and thread-safe) • Maintainable(180->20, no
need for tests(builder), faster PRs, debuggable) Pros/Cons -
Pros/Cons • DRY • Immutable(Type-safe, null-safe and thread-safe) • Maintainable(180->20,
no need for tests(builder), faster PRs, debuggable) • API-invisible -
Pros/Cons • DRY • Immutable(Type-safe, null-safe and thread-safe) • Maintainable(180->20,
no need for tests(builder), faster PRs, debuggable) • API-invisible • No runtime dependencies -
Pros/Cons • DRY • Immutable(Type-safe, null-safe and thread-safe) • Maintainable(180->20,
no need for tests(builder), faster PRs, debuggable) • API-invisible • No runtime dependencies • Docs -
Cons • Require to compile • Fabrics for class instantiation
may break • Mutable fields, arrays, lists -
Links and contacts AutoValue - https://github.com/google/auto/blob/master/value/userguide/index.md AutoValue plugins for AS
- https://github.com/afcastano/AutoValuePlugin https://github.com/robohorse/RoboPOJOGenerator AutoValue Extensions - https://github.com/blipinsk/awesome-auto-value-extensions @Eliminate("Boilerplate") by Ryan Harter - https://www.youtube.com/watch?v=NBkl_SIHUr8 AutoValue by Jake Wharton - https://www.youtube.com/watch?v=FfBBTHkRC-o