Java. Generalizations. Parameterized types. Generic classes, interfaces, methods




Generalizations. Parameterized types. Generic classes, interfaces, methods


Contents


Search other websites:

1. What is “generalization” in Java? What is a parameterized type? Specifics of using generics

Generalization is a mechanism for building a program code for a certain type with an arbitrary name in order to further convert it to another specific reference type. Conversion from a generic type to another (specific) type is implemented by the compiler.

Generics can be applied to classes, interfaces, or methods. If a class, interface or method operates on some generic type T, then this class (interface, method) is called generic. A type that receives a generic class as a parameter is called a parameterized type.

Generics can be applied to classes, interfaces, or methods. If a class, interface or method operates on some generic type T, then this class (interface, method) is called generic. A type that receives a generic class as a parameter is called a parameterized type. The name of a parameterized type can be anything (T, Type, TT, etc.).

 

2. The advantages of using generalizations

Using generics in Java provides the following benefits:

  • compactness of the program code is provided;
  • thanks to generalizations, all casts are done automatically and implicitly. Reusable code is easier and safer to handle;
  • generalizations provide type safety as opposed to using references to Object type. This means that using a reference to the Object class, you can operate with different types of objects. However, this method does not provide type safety.

 

3. The general form of declaring a generic class and declaring a reference to a generic class

For a method of a class to operate on the generic type Type, the class (interface) must be declared as generic. The Java language allows a class to be declared for one or more generic types. The general form of a class that uses generic types is as follows:

class ClassName<Type1, Type2, ..., TypeN> {
    // ...
}

here

  • ClassName – the nsme of class;
  • Type1, Type2, TypeN – the types that the ClassName class operates on.

Most often, a generic class operates with one type. In this case, the general form of the class is:

class ClassName<Type> {
    // ...
}

The general form of declaring a reference to a generic class is as follows

ClassName<Type1, Type2, ..., TypeN> VarName =
    new ClassName<Type1, Type2, ..., Type>(argument_list);

where

  • ClassName – the name of class;
  • VarName – the name of the reference to the instance of the class that is created by the new operator;
  • argument_list – a list of arguments that the class constructor receives.

For example, if you declare a class in the program that receives the type T as a parameter

class SomeClass<T> {

}

then in this class you can implement variables and methods that are of type T

class SomeClass<T> {
  // Declaring variables with type T
  T var1, var2, ..., varN;

  // Method declaration using type T
  public return_type SomeMethod(T param1, T param2, ..., T paramN) {
    // Implementation of the SomeMethod() method using type T
    // ...
  }
}

After the declaration, the use of the above class for Integer type will be as follows

SomeClass<Integer> objInt = new SomeClass<Integer>();
objInt.SomeMethod(arg1, arg2, ..., argN);

In the above declaration, the type Integer is a type argument.

Instantiating a class for type Double is as follows

SomeClass<Double> objDouble = new SomeClass<Double>();

Likewise, the generic class SomeClass<T> can be used for any other type.

 

4. What types are prohibited from being used in generic classes as parameterized types?

When using generic classes, the type parameter must only be of a reference type. When using a generic class, it is forbidden to use base types (int, char, double, etc.) as a type argument.

That is, if the following class is specified

class SomeClass<T> {
  // ...
}

then you will not be able to declare an instance of type int or other base type

SomeClass<int> objInt = new SomeClass<int>(); // Error

Instead of int, you must use the Integer wrapper class.

Similarly, for other base types, you need to use the wrapper classes: double -> Double, float-> Float, boolean-> Boolean, etc.

 

5. An example of a generic class that implements a method for finding an element in a two-dimensional matrix

The generic class GenericMatrix<Type> is declared, which implements the SearchKey() method, which calculates the number of occurrences of the given key element in the matrix.

The SearchKey() method receives the following parameters:

  • M – the initial matrix of the generic type Type[][], in which the key is searched;
  • m, n – the dimension of the matrix, the number of rows and columns, respectively;
  • key – a key (element), the number of occurrences of which in the matrix M needs to be calculated.

 

// Generic class operating on Type type
class GenericMatrix<Type> {
  // A method that counts the number of key elements in a two-dimensional matrix M.
  public int SearchKey(Type[][] M, int m, int n, Type key) {
    int count = 0;
    for (int i=0; i<m; i++)
      for (int j=0; j<n; j++)
        if (key==M[i][j])
          count++;
    return count;
  }
}

public class TestGeneric<Type> {

  public static void main(String[] args) {
    // Demonstration of using the SearchKey method in the GenericMatrix<Type> class
    // Declaring internal variables
    int count;

    // Source matrix
    Integer[][] MI = {
      { 2, 5, -8 },
      { 3, 1, 5 },
      { 4, 8, 2 },
      { 5, 1, 8 }
    };

    // Create an instance of the GenericMatrix<Type> class
    GenericMatrix<Integer> obj = new GenericMatrix<Integer>();

    // Call the SearchKey() method of the instance
    count = obj.SearchKey(MI, 4, 3, 8);

    // Print the result
    System.out.println("count = " + count); // count = 2
  }
}

 

6. An example implementation of a method that performs a cyclic shift in an array of the generic type Type

The example implements the GenericClass<Type> class, which contains the following methods:

  • CyclicShift() – implements a cyclic shift in a one-dimensional array, the elements of which are of the generalized type Type;
  • Print() – implements the output of an array of the generic type Type to the screen;
  • main() – demonstrates the use of the class.

 

// A class that has a cyclic shift method in an array of the generic Type
public class GenericClass<Type> {
  // Cyclic shift in array A by count positions.
  // Parameters:
  // - A - source array;
  // - count - the number of positions to shift;
  // - direction - direction (true - left, false - right).
  public void CyclicShift(Type[] A, int count, boolean direction)
  {
    Type item;
    int iterations = (int)(count % A.length); // remove unnecessary iterations

    if (direction)
    {
      // Shift left
      for (int i=0; i<iterations; i++)
      {
        item = A[0];
        for (int j=0; j<A.length-1; j++)
          A[j] = A[j+1];
        A[A.length-1] = item;
      }
    }
    else
    {
      // Shift right
      for (int i=0; i<iterations; i++)
      {
        item = A[A.length-1];
        for (int j=A.length-2; j>=0; j--)
          A[j+1] = A[j];
        A[0] = item;
      }
    }
  }

  // A method that outputs an array of the generic Type with the comment text
  public void Print(Type[] A, String text) {
    System.out.println(text);
    for (int i=0; i<A.length; i++) {
      System.out.print(A[i] + " | ");
    }
    System.out.println();
  }

  public static void main(String[] args) {
    //Demonstration of using the CyclicShift() method in the TestGeneric<Type> class
    // for type String
    // Source array
    String[] AS = { "1-abc", "2-cde", "3-fgh", "4-jklmn", "5-jprst" };

    // Declare an instance of TestGeneric<Type> class
    TestGeneric<String> obj = new TestGeneric<String>();

    // Print ghe array AS
    obj.Print(AS, "Array AS. Before: ");

    // Implement circular shift for type String
    obj.CyclicShift(AS, 3, true); // shift 3 positions to the left

    // Print the AS array again
    obj.Print(AS, "Array AS. After:");
    // -------------------------------------

    // Using CyclicShift() method for Double reference type
    Double[] AD = { 1.5, 1.3, 1.1, 0.8, 0.5, 0.2 };
    TestGeneric<Double> objD = new TestGeneric<Double>();

    objD.Print(AD, "Array AD. Before: ");
    objD.CyclicShift(AD, 2, false); // Move 2 positions to the right
    objD.Print(AD, "Array AD. After:");

    // Using Cyclic Shift() method for Integer reference type
    Integer[] AI = { 5, 3, 2, -1, -8, 4, 3, 0, 12 };
    TestGeneric<Integer> objI = new TestGeneric<Integer>();
    objI.Print(AI, "Array AI. Before:");
    objI.CyclicShift(AI, 6, true); // shift 6 positions to the left
    objI.Print(AI, "Array AI. After: ");
  }
}

The program result

Array AS. Before:
1-abc | 2-cde | 3-fgh | 4-jklmn | 5-jprst |
Array AS. After:
4-jklmn | 5-jprst | 1-abc | 2-cde | 3-fgh |
Array AD. Before:
1.5 | 1.3 | 1.1 | 0.8 | 0.5 | 0.2 |
Array AD. After:
0.5 | 0.2 | 1.5 | 1.3 | 1.1 | 0.8 |
Array AI. Before:
5 | 3 | 2 | -1 | -8 | 4 | 3 | 0 | 12 |
Array AI. After:
3 | 0 | 12 | 5 | 3 | 2 | -1 | -8 | 4 |

 

7. An example of a class that receives two parameterized types

A class can receive several types as parameters. Below is an example of a class that takes two types T1, T2 as parameters. The class implements the Print() method, which outputs the value of arrays of elements of types T1 and T2.

// A class that takes as parameters two types T1, T2
public class GenericClass<T1, T2> {

  // Method that prints single elements of types T1, T2
  public void Print(T1 item1, T2 item2, String comment) {
    System.out.println(comment);
    System.out.println("item1 = " + item1);
    System.out.println("item2 = " + item2);
    System.out.println("The type of item1 = " + item1.getClass());
    System.out.println("The type of item2 = " + item2.getClass());
  }

  // Method that prints arrays of elements of generic types T1, T2
  public void Print(T1[] A1, T2[] A2, String comment) {
    System.out.println(comment);

    System.out.print("A1 = { ");
    for (int i=0; i<A1.length; i++)
      System.out.print(A1[i] + " ");
    System.out.println(" }");

    System.out.print("A2 = { ");
    for (int i=0; i<A2.length; i++)
      System.out.print(A2[i] + " ");
    System.out.println(" }");

    System.out.println("The type of array A1 = " + A1.getClass());
    System.out.println("Tye type of array A2 = " + A2.getClass());
  }

  public static void main(String[] args) {
    // Demonstration of using the GenericClass<T1, T2> class that takes two type parameters
    // 1. Using the Print() method for single elements of different types
    // 1.1. Declare variables of different types
    Integer item1 = 23;
    Double item2 = 2.85;

    // 1.2. Declare the obj1 instance
    GenericClass<Integer, Double> obj1 = new GenericClass<Integer, Double>();

    // 1.3. Invoke Print() method on single items
    obj1.Print(item1, item2, "The values of item1 and item2:");

    // 2. Using method Print() for arrays
    // 2.1. Declare arrays
    Float[] AF = { 3.8f, 2.5f, -1.4f, 2.2f, 0.001f };
    Boolean[] AB = { true, true, false, true, false };

    // 2.2. Declare instance obj2
    GenericClass<Float, Boolean> obj2 = new GenericClass<Float, Boolean>();

    // 2.3. Invoke Print() for arrays
    obj2.Print(AF, AB, "The values of arrays AF, AB");
  }
}

The result of the program

The values of item1 and item2:
item1 = 23
item2 = 2.85
The type of item1 = class java.lang.Integer
The type of item2 = class java.lang.Double
The values of arrays AF, AB
A1 = { 3.8 2.5 -1.4   2.2 0.001   }
A2 = { true true false   true false   }
The type of array A1 = class [Ljava.lang.Float;
Tye type of array A2 = class [Ljava.lang.Boolean;

 


Related topics