Link

Classes

Table of contents

  1. Generic types

Generic types

We can use generics with classes too. Consider the following example.

package demo;

import java.awt.Point;
import java.math.BigDecimal;

public class App {

  public static void main( final String[] args ) {
    final String childName = "Jade";
    final int childAge = 13;

    final String productName = "Crisps";
    final BigDecimal productPrice = new BigDecimal( "1.99" );

    final String originName = "Origin";
    final Point originPoint = new Point( 0, 0 );

    System.out.printf( "%s: %s%n", childName, childAge );
    System.out.printf( "%s: %s%n", productName, productPrice );
    System.out.printf( "%s: %s%n", originName, originPoint );
  }
}

The above example has three pairs of values.

  • A child name and age
  • A product and its price
  • A point and its coordinates

These two pairs of variables can be bound together by a class, Pair. Before generics this was annoying as we had to manually cast the type and the compiler did not provide any safety, as discussed in the raw types section. Generics made this simpler, as shown next.

package demo;

import lombok.Data;

@Data(staticConstructor = "of")
public class Pair<N, V> {

  private final N name;
  private final V value;
}

The Pair class, shown above, is an immutable class that has two generic types.

package demo;

import java.awt.Point;
import java.math.BigDecimal;

public class App {

  public static void main( final String[] args ) {
    final Pair<String, Integer> child = Pair.of( "Jade", 13 );
    final Pair<String, BigDecimal> product = Pair.of( "Crisps", new BigDecimal( "1.99" ) );
    final Pair<String, Point> origin = Pair.of( "Origin", new Point( 0, 0 ) );

    System.out.printf( "%s%n", child );
    System.out.printf( "%s%n", product );
    System.out.printf( "%s%n", origin );
  }
}

Each pair of variables, such as childName and childAge, are now grouped into one variable, such as child.

  • From
         final String childName = "Jade";
         final int childAge = 13;
    
  • To
         final Pair<String, Integer> child = Pair.of( "Jade", 13 );
    

The example can be simplified further, by using the var as shown next.

package demo;

import java.awt.Point;

public class App {

  public static void main( final String[] args ) {
    final var child = Pair.of( "Jade", 13 );
    final var product = Pair.of( "Crisps", new BigDecimal( "1.99" ) );
    final var origin = Pair.of( "Origin", new Point( 0, 0 ) );

    System.out.printf( "%s%n", child );
    System.out.printf( "%s%n", product );
    System.out.printf( "%s%n", origin );
  }
}

The example will print.

Pair(name=Jade, value=13)
Pair(name=Crisps, value=1.99)
Pair(name=Origin, value=java.awt.Point[x=0,y=0])

Our new class can be used in another generic context, as shown next.

package demo;

import java.util.ArrayList;
import java.util.List;

public class App {

  public static void main( final String[] args ) {
    final List<Pair<String, Integer>> children = new ArrayList<>();
    children.add( Pair.of( "Jade", 13 ) );
    children.add( Pair.of( "Aden", 11 ) );

    System.out.printf( "Children: %s%n", children );
  }
}

The type of the children variable is quite verbose, List<Pair<String, Integer>>, and we may be tempted to use var instead, as shown next.

⚠ Proceed with caution!!The list named, children can contain any Object.
package demo;

import java.util.ArrayList;
import java.util.List;

public class App {

  public static void main( final String[] args ) {
    final var children = new ArrayList<>();
    children.add( Pair.of( "Jade", 13 ) );
    children.add( Pair.of( "Aden", 11 ) );

    System.out.printf( "Children: %s%n", children );
  }
}

As hinted already in a previous example in this page, this is not the same as the one that defined the type. Using var in this case, the children variable is now an ArrayList of type Object.