Transform object into another type with Java 8

Quite often when calling to get external data you need to convert an object returned into a internal class. Learn how to transform an object into another type by using java 8 function and stream's map.

Detailed Video Notes

Applications may need to translate an object from one system domain to another. For instance, you may make a rest web service request, cross business unit native call or vendor request that returns a Location object which you need to map to the internal MyLocation class. In episode 2 we walked through transforming from one object to another with guava, in this episode we will examine how to transform an object into another type with java 8.

Getting started

[0:26]

Before we get started, there is two concepts to cover. First is a java.util.function.Function which accepts one argument and produces a result. The second is stream intermediate operation map which converts each element in a stream into another object via the supplied function. The method construct below where T is the type of input to the function and R is the result of the function. So T is what goes in and R is what comes out.

Function<T, R> myFunction = new Function<T, R>() {
    public R apply(T t) {
        return null;
    }
};

Throughout this tutorial we will reference two locations objects, GoogleGeoLocation which represents an external data object and MyLocation which is the class for our internal system.

GoogleGeoLocation

public class GoogleGeoLocation {

    private Integer homeMobileCountryCode;
    private Integer homeMobileNetworkCode;
    private String radioType;
    private String carrier;

    /...
}    

MyLocation

public class MyLocation {

    private Double countryCode;
    private Double networkCode;
    private String myRadioType;
    private String myCarrier;

    //...
}

Converting a single object

[1:3]

The first step in converting an object to another is defining a function. Mentioned earlier, a function will accept a type of GoogleLocation and will return an instance of MyLocation. Inside this method we will create a new object of MyLocation and set the values passed from GoogleGeoLocation. Our mapping between objects is pretty straight forward but you could apply various mapping or business rules. Calling function.apply will apply the function to the given argument.

Function<GoogleGeoLocation, MyLocation> externalToMyLocation
    = new Function<GoogleGeoLocation, MyLocation>() {

    public MyLocation apply(GoogleGeoLocation t) {
        MyLocation myLocation = new MyLocation();
        myLocation.setCountryCode(Double.valueOf(t
                .getHomeMobileCountryCode()));
        myLocation.setMyCarrier(t.getCarrier());
        myLocation.setMyRadioType(t.getRadioType());
        myLocation.setNetworkCode(Double.valueOf(t
                .getHomeMobileNetworkCode()));

        return myLocation;
    }
};

@Test
public void convertSingleObject() {

    GoogleGeoLocation googleLocation = new GoogleGeoLocation(310, 410,
            "gsm", "Vodafone");

    MyLocation myLocation = externalToMyLocation.apply(googleLocation);

    assertEquals(myLocation.getMyRadioType(), googleLocation.getRadioType());
}

Output

MyLocation [countryCode=310.0, networkCode=410.0, myRadioType=gsm, myCarrier=Vodafone]

Create a list of objects from another

[1:32]

Next you might be dealing with a collection of objects to convert. Let's seed our data with random google locations and call java.util.stream.Stream() on the list. A java 8 stream represents a sequence of elements in which one or more operations can be performed such as reduction and aggregate operations. As we pointed out above we will call the map and pass in the function to transform the list. Finally calling collect we will instruct the stream to return a List of myLocations.

@Test
public void convertCollection() {

    GoogleGeoLocation googleLocation1 = new GoogleGeoLocation(310, 410,
            "gsm", "Vodafone");

    GoogleGeoLocation googleLocation2 = new GoogleGeoLocation(222, 777,
            "gprs", "Verizon");

    GoogleGeoLocation googleLocation3 = new GoogleGeoLocation(111, 234,
            "gsm", "Sprint");

    GoogleGeoLocation googleLocation4 = new GoogleGeoLocation(345, 567,
            "gprs", "Us Cell");

    List<GoogleGeoLocation> gLocations = new ArrayList<GoogleGeoLocation>();
    gLocations.add(googleLocation1);
    gLocations.add(googleLocation2);
    gLocations.add(googleLocation3);
    gLocations.add(googleLocation4);

    List<MyLocation> myLocations = gLocations.stream()
            .map(externalToMyLocation)
            .collect(Collectors.<MyLocation> toList());

    System.out.println(myLocations);
}

Output

[MyLocation [countryCode=310.0, networkCode=410.0, myRadioType=gsm, myCarrier=Vodafone], MyLocation [countryCode=222.0, networkCode=777.0, myRadioType=gprs, myCarrier=Verizon], MyLocation [countryCode=111.0, networkCode=234.0, myRadioType=gsm, myCarrier=Sprint], MyLocation [countryCode=345.0, networkCode=567.0, myRadioType=gprs, myCarrier=Us Cell]]

Using a familiar way

[1:57]

You might be asking, why not use the standard foreach construct. Using a function promotes cleaner concise code, reusable method, promotes thinking in a functional manner and much easier to unit test. Just think, if we didn't use a function we would have copied the same logic to convert a single object and the ArrayList. To show the contrast we wrote out a familiar way of pilling all the logic in a single for loop.

List<MyLocation> myLocations = new ArrayList<MyLocation>();

for (GoogleGeoLocation externalLocation: gLocations) {

    MyLocation myLocation = new MyLocation();
    myLocation.setCountryCode(Double.valueOf(externalLocation
            .getHomeMobileCountryCode()));
    myLocation.setMyCarrier(externalLocation.getCarrier());
    myLocation.setMyRadioType(externalLocation.getRadioType());
    myLocation.setNetworkCode(Double.valueOf(externalLocation
            .getHomeMobileNetworkCode()));

    myLocations.add(myLocation);
}

System.out.println(myLocations);

Hope you enjoyed today's level up, have a great day!