Java. Ensuring type safety by using generalizations. Generalized interfaces. Examples

Ensuring type safety by using generalizations. Generalized interfaces. Examples


Contents


Search other websites:




1. The safety of types. Ensuring type safety by using generalizations. Example

The main advantage of generalizations – ensuring type safety. If a program uses generics and contains incorrect typecasting code (for example, casting a string to a number), then the compiler will fix the error at the compilation stage and not at the runtime. If an error is found at compile time, it is better than detecting the error at runtime. Generating a run-time error is invalid.

In Java, the superclass for all classes is Object. This makes it possible to declare references to the Object type and use them to cast to any type. However, this approach does not provide type safety, as a result of which errors will compile and occur at runtime, which is unacceptable.

Example. The example shows the following advantages of using generics versus using a reference to the Object type:

  • when using generics for basic types (int, double, float and others), you do not need to perform an explicit type conversion. When using a reference to the Object type, explicit casting is required;
  • using generalizations ensured type safety (incompatible assignment errors are determined at compile time). Using a reference to the type Object is not type-safe (incompatible assignment errors are determined at runtime).

Two classes are given:

  • class ClassObject – uses a reference to the type Object. Since the Object type is basic for all types (classes), this reference can be cast to any other type;
  • generic class ClassGen<T>, which uses a parameterized type T.
// A class that uses a reference to the Object type
class ClassObject {
  // Some value - is of type Object
  private Object value;

  // Constructor
  ClassObject(Object _value) {
    value = _value;
  }

  // Access methods
  Object GetValue() {
    return value;
  }

  void SetValue(Object _value) {
    value = _value;
  }
}

// Generic Class - provides type safety,
// the error occurs at compile time, not at run time.
class ClassGen<T> {
  private T value;

  // Constructor
  public ClassGen(T value) {
    this.value = value;
  }

  // Access methods
  public T GetValue() {
    return value;
  }

  void SetValue(T value) {
    this.value = value;
  }
}

The third class Test implements two static methods Test_ClassObject() and TestClassGen(). These methods deliberately perform an erroneous operation of assigning to the number objInt the string objString

objInt = objString;

here

  • objInt – the object of type Integer (number);
  • objString – the object of type String (string).

In the Test_ClassGen() method, this erroneous operation is detected at compile time. That is, the execution of the program will not take place until this error is fixed. In this case, the standard security mechanism is triggered.

In the Test_ClassObject() method, the error is detected at runtime (the compilation phase does not detect the error). That is, the typical safety mechanism does not work. And this is unacceptable.

public class Test {
  // Method that tests the class in which a reference of type Object is declared
  public static void Test_ClassObject() {
    // Create an instance of type Integer and assign its value to a variable of type int
    ClassObject objInt = new ClassObject(25);
    int vInt = (Integer)objInt.GetValue(); // here you need to typecast

    // Create an instance of type String
    ClassObject objString = new ClassObject("Hello");
    String vString = (String)objString.GetValue(); // here you also need an explicit casting

    // Assign a variable of type Integer to a variable of type String
    objInt = objString; // the compiler does not issue an error here - this is wrong
    int value = (Integer)objInt.GetValue(); // runtime error!!!
  }

  // Class that uses the generic mechanism
  public static void Test_ClassGen() {

    // Create an instance of type Integer
    ClassGen<Integer> objInt = new ClassGen<Integer>(25);
    int vInt = objInt.GetValue(); // there is no need typecast

    // Create an instance of type String
    ClassGen<String> objString = new ClassGen<String>("Hello");
    String vString = objString.GetValue(); // there is also no need to typecast

    // Set Integer to String
    objInt = objString; // compile-time error - type safety triggered
  }

  public static void main(String[] args) {
    // Calling test methods
    Test_ClassObject();
    Test_ClassGen();
  }
}

 

2. Generalized interface. Features of use

The Java language allows you to declare an interface that uses one or more generic types. The general form of declaring a generic interface named InterfaceName that uses type T is as follows:

interface InterfaceName<T> {
  ...
}

The general form of a generic interface that uses several generic types is as follows:

interface InterfaceName<T1, T2, ..., TN> {
  ...
}

here

  • InterfaceName – the name of a generic interface that uses types T1, T2, …, TN.

 

3. An example of declaring and using a generic interface with one type parameter

The example declares one generic interface IValue<T> and a generic class Value<T>. The interface and the class get the type T. The interface declares the Show() method, which is implemented in the class. The class contains an internal variable value.

The GenInterface class implements two static functions that demonstrate the use of the IValue<T> interface and the Value<T> class.

// An interface using one parameter of type T
interface IValue<T> {
  // Methods to access the value
  T GetValue();
  void SetValue(T value);

  // Show() method - displays information about the value
  void Show();
}

// Generic class that implements the IValue<T> interface
class Value<T> implements IValue<T> {
  // Internal class variables
  T value;

  // Constructor
  public Value(T _value) {
    value = _value;
  }

  // Implementing interface methods
  public T GetValue() {
    return value;
  }

  public void SetValue(T value) {
    this.value = value;
  }

  // Implementing the Show() method of the interface
  public void Show() {
    System.out.println("value = " + value);
  }
}

public class GenInterface {
  // Method that gets a reference to the IValue interface
  public static void ShowValue(IValue ref) {
    ref.Show();
  }

  public static void main(String[] args) {
    // 1. Declaring a reference to the interface IValue<T>
    IValue<Double> refV;

    // 2. Create an instance of class Value<T>
    Value<Double> obj = new Value<Double>(5.88);

    // 3. Checking the method GetValue() of class Value<T>
    double t = obj.GetValue();
    System.out.println("t = " + t); // t = 5.88

    // 4. Checking the method SetValue()
    obj.SetValue(7.777);
    t = obj.GetValue();
    System.out.println("t = " + t); // t = 7.777

    // 5. Checking the method Show()
    obj.Show();   // value = 7.777

    // 6. Calling the static ShowValue() method with a refV interface reference parameter
    refV = obj; // Assign the address of an instance of the Value <T> class to the reference
    ShowValue(refV); // value = 7.777

    // 7. Calling the static ShowValue() method with an instance parameter obj
    ShowValue(obj); // value = 7.777
  }
}

The result of the program

t = 5.88
t = 7.777
value = 7.777
value = 7.777
value = 7.777

 

4. A simple example of declaring a generic interface and using it in a generic class that uses 3 parameters like T1, T2, T3

This example is a demonstration of the declaration and use of generic interfaces in the Java language.

The example demonstrates the use of a generic interface named MyInterface. The interface declares methods that operate on three types T1, T2, T3. The class MyClass<T1, T2, T3> is also declared, which implements the interface MyInterface<T1, T2, T3>. Three internal variables are declared in the class, which have the types T1, T2, T3, respectively.

// Interface that uses 3 generic types
interface MyInterface<T1, T2, T3> {
  // The interface specifies methods that use the types T1, T2, T3
  // Return the value of variables of types T1, T2, T3
  T1 GetT1();
  T2 GetT2();
  T3 GetT3();

  // Set values of variables of types T1, T2, T3
  void SetT1(T1 obj);
  void SetT2(T2 obj);
  void SetT3(T3 obj);

  // Variable values output method
  void Print(String text);
}

// A class that implements the MyInterface<T1, T2, T3> interface.
// The class declaration necessarily need to specify the names of the types T1, T2, T3
class MyClass<T1, T2, T3> implements MyInterface<T1, T2, T3> {
  // Internal class variables
  T1 var1;
  T2 var2;
  T3 var3;

  // Class constructor
  public MyClass(T1 var1, T2 var2, T3 var3) {
    this.var1 = var1;
    this.var2 = var2;
    this.var3 = var3;
  }

  // Methods that are declared in the interface MyInterface<T1, T2, T3>
  public T1 GetT1() {
    return var1;
  }

  public T2 GetT2() {
    return var2;
  }

  public T3 GetT3() {
    return var3;
  }

  public void SetT1(T1 var1) {
    this.var1 = var1;
  }

  public void SetT2(T2 v2) {
    var2 = v2;
  }

  public void SetT3(T3 v3) {
    this.var3 = v3;
  }

  public void Print(String comment) {
    System.out.println(comment);
    System.out.println("var1 = " + var1);
    System.out.println("var2 = " + var2);
    System.out.println("var3 = " + var3);
  }
}

public class GenInterface {
  public static void main(String[] args) {
    // Demonstration of using the generic interface MyInterface <T1, T2, T3>
    // 1. Declare a reference to an interface that uses the types Integer, Character, and Long
    MyInterface<Integer, Character, Long> ref;

    // 2. Declare an instance of the class MyClass that handles the types Integer, Character, Long
    MyClass<Integer, Character, Long> obj = new MyClass<Integer, Character, Long>(25, 'z', 3223232332L);

    // 3. Get a value of type Integer (int) - GetT1() method
    int vInt = obj.GetT1();
    System.out.println("vInt = " + vInt);

    // 4. Get a value of type Character (char) - GetT2 () method
    char vChar1 = obj.GetT2();
    Character vChar2 = obj.GetT2(); // so you can too
    System.out.println("vChar1 = " + vChar1);
    System.out.println("vChar2 = " + vChar2);

    // 5. Get a Long (long) value - GetT3() method
    long vLong;
    vLong = obj.GetT3();
    System.out.println("vLong = " + vLong);

    // 6. Print all internal variables - Print() method
    obj.Print("-----------------");
  }
}

The result of the program

vInt = 25
vChar1 = z
vChar2 = z
vLong = 3223232332
-----------------
var1 = 25
var2 = z
var3 = 3223232332

 


Related topics