An extension for Google's AutoValue that generates a few handy methods to determine object variations based on statically defined variance groups.
Consider the following class:
@AutoValue abstract class Car{
abstract String manufacturer();
abstract String model();
abstract String plateNumber();
abstract int color();
abstract String body();
}
Having a list of cars and a reference car, lets filter all the cars that have the manufacturer and model fields equal to the ones defined in our reference. Most probably we'll end up with some code similar to this:
Car ref = new AutoValue_Car("Tesla", "Model S", "", 0, "");
for(Car car : cars){
boolean match = (ref.manufacturer() == null ? car.manufacturer() == null : ref.manufacturer().equals(car.manufacturer()))
&& (ref.model() == null ? car.model() == null : ref.model().equals(car.model()))
if(match){
filtered.add(car);
}
}
This code is hard to read and maintain and can get worse if filters get more complex.
Two objects are variants if they are of the same type and are not equal except for a set of their properties.
Lets call an object A variant of object B if the followings apply:
- A and B are of the same type
- A and B have a set F of fields that are considered constant
- Each property from F(A) equals to its corresponding property in F(B)
A.equals(B)
returnsfalse
AutoValue Variant has two key elements:
Variant
interface, containing the methods for determining variants@NonVariant
annotation, applied to fields that we want to consider constant
Implement Variant
, then apply the @NonVariant
annotation on fields you want to consider constant.
@AutoValue abstract class Car implements Variant{
@NonVariant abstract String manufacturer();
@NonVariant abstract String model();
abstract String plateNumber();
abstract int color();
abstract String body();
}
Then filtering will look like this:
for(Car car : cars){
if(ref.like(car)){
filtered.add(car);
}
}
// or
filtered = cars.stream().filter(car -> ref.like(car));
You can define multiple variant groups by specifying the group name when applying the @NonVariant
annotation
public abstract class Car implements Variant<Car> {
@NonVariant(VariantGroups.IDENTITY)
abstract String manufacturer();
@NonVariant({VariantGroups.IDENTITY, VariantGroups.MODEL_AND_BODY})
abstract String model();
abstract String plateNumber();
@NonVariant(VariantGroups.ASPECT)
abstract int color();
@NonVariant({VariantGroups.ASPECT, VariantGroups.MODEL_AND_BODY})
abstract String body();
static class VariantGroups {
static final String IDENTITY = "identity";
static final String ASPECT = "aspect";
static final String MODEL_AND_BODY = "model_and_body";
}
}
Then apply the like(Object, String)
method
Car ref = new AutoValue_Car("Tesla", "Model S", "", 0xFF000000, "Sedan");
similarTeslaModelSCars = cars.stream().filter(car -> ref.like(car, Car.VariantGroups.IDENTITY));
blackSedans = cars.stream().filter(car -> ref.like(car, Car.VariantGroups.ASPECT));
annotationProcessor 'com.ccheptea.auto.value:auto-value-variant:1.2.0'
compile 'com.ccheptea.auto.value:auto-value-variant-runtime:1.2.0'
Snapshots of the development version are available in Sonatype's snapshots
repository.
The code for auto deploying artifacts with Travis as well as some of the extension code is based on or inspired by parts of:
- [AutoValue: Redacted Extension] (https://github.com/square/auto-value-redacted)
- [AutoValue: Parcel Extension] (https://github.com/rharter/auto-value-parcel)
Copyright 2017 Constantin Cheptea.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.