Java Composition Example

Category: Java   Tags: Java, Java Basic, Java Composition

Composition is about using instance variables that are references to other objects. For example:

                          class A {

                          }

                          class B {
                            private A a = new A();

                          }
                        

Class B is related to class A by composition, because B has an instance variable that holds a reference to a A object.

Problem With Inheritance

Consider this example:

CompositionExample1.java

                          package com.tutorial.javabasic;

                          class Vehicle {

                              public String getDetails() {

                                  return "This is vehicle details";
                              }
                          }

                          class Car extends Vehicle {
                          }

                          public class CompositionExample1 {

                              public static void main(String s[]) {
                                  Car c = new Car();
                                  String details = c.getDetails();
                                  System.out.println(details);
                              }
                          }
                        

Output:

                          This is vehicle details
                        

This example will work fine. Now let say we wish to change the return type of getDetails method in future to type VehicleDetails. Here is how it will look:

CompositionExample2.java

                          package com.tutorial.javabasic;

                          class VehicleDetails {
                              private String details;

                              public String getDetails() {
                                  return details;
                              }

                              public void setDetails(String details) {
                                  this.details = details;
                              }

                          }

                          class Vehicle {

                              public VehicleDetails getDetails() {
                                  VehicleDetails vehicleDetails = new VehicleDetails();
                                  vehicleDetails.setDetails("This is vehicle details");
                                  return vehicleDetails;
                              }
                          }

                          class Car extends Vehicle {
                          }

                          public class CompositionExample2 {

                              public static void main(String s[]) {
                                  Car c = new Car();
                                  /*
                                  This will not compile. Error:
                                  incompatible types: VehicleDetails cannot be converted to String
                                  */
                                  String details = c.getDetails();
                                  System.out.println(details);
                              }
                          }
                        

Here we have introduce a new class VehicleDetails and changed the return type of getDetails() method to VehicleDetails. Changing return type will break the code inside CompositionExample2 class as the line String details = c.getDetails(); is expecting a string return type but now it has changed to VehicleDetails.

So what we see here, modifying parent class is breaking its subclass. To overcome this problem, composition was introduced.

Composition in Rescue

Here is how composition will solve the above problem caused by inheritance:

CompositionExample3.java

                          package com.tutorial.javabasic;
                          class VehicleDetails {

                              private String details;

                              public String getDetails() {
                                  return details;
                              }

                              public void setDetails(String details) {
                                  this.details = details;
                              }

                          }

                          class Vehicle {

                              public VehicleDetails getDetails() {
                                  VehicleDetails vehicleDetails = new VehicleDetails();
                                  vehicleDetails.setDetails("This is vehicle details");
                                  return vehicleDetails;
                              }
                          }

                          class Car {
                              Vehicle vehicle = new Vehicle();

                              public String getDetails() {
                                  VehicleDetails vehicleDetails = vehicle.getDetails();
                                  return vehicleDetails.getDetails();
                              }
                          }

                          public class CompositionExample3 {

                              public static void main(String s[]) {
                                  Car c = new Car();
                                  String details = c.getDetails();
                                  System.out.println(details);
                              }
                          }
                        

See the changes inside Car class. Instead of extending the class Vehicle, we are creating an instance of Vehicle. Using this instance to return the vehicle details. Everything inside main() method is unchanged and unbroken.