Java. Обеспечение типовой безопасности с помощью обобщений. Обобщенные интерфейсы. Примеры

Обеспечение типовой безопасности с помощью обобщений. Обобщенные интерфейсы. Примеры


Содержание


Поиск на других ресурсах:




1. Типовая безопасность. Обеспечение типовой безопасности с помощью обобщений. Пример

Основное преимущество обобщений – обеспечение типовой безопасности. Если в программе используются обобщения и содержится некорректный код приведения типов (например, приведение строки в число), то компилятор фиксирует ошибку на этапе компиляции а не на этапе выполнения. Если ошибка найдена на этапе компиляции, то это лучше чем определение ошибки на этапе выполнения. Генерирование ошибки на этапе выполнения есть недопустимым.

В языке Java суперклассом для всех классов есть класс Object. Это дает возможность объявлять ссылки на тип Object и использовать их для приведения к любому типу. Однако, такой подход не обеспечивает типовой безопасности, в результате чего ошибки будут компилироваться и возникать на этапе выполнения, а это есть недопустимо.

Пример. В примере показаны следующие преимущества применения обобщений в сравнении с использованием ссылки на тип Object:

  • при использовании обобщений для базовых типов (int, double, float и других) не нужно выполнять явное приведение типа. При использовании ссылки на тип Object явное приведение есть обязательным;
  • при использовании обобщений обеспечивается типовая безопасность (ошибки несовместимого присваивания определяются на этапе компиляции). При использовании ссылки на тип Object типовая безопасность не обеспечивается (ошибки несовместимого присваивания определяются на этапе выполнения).

Заданы два класса:

  • класс ClassObject – использует ссылку на тип Object. Поскольку тип Object есть базовым для всех типов (классов), то эту ссылку можно привести к любому другому типу;
  • обобщенный класс ClassGen<T>, использующий параметризованный тип T.
// Класс, который использует ссылку на тип Object
class ClassObject {

  // Некоторое значение - имеет тип Object
  private Object value;

  // Конструктор
  ClassObject(Object _value) {
    value = _value;
  }

  // Методы доступа
  Object GetValue() {
    return value;
  }

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

// Обобщенный класс - обеспечивает типовую безопасность,
// ошибка возникает на этапе компиляции, а не на этапе выполнения.
class ClassGen<T> {
  private T value;

  // Конструктор
  public ClassGen(T value) {
    this.value = value;
  }

  // Методы доступа
  public T GetValue() {
    return value;
  }

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

В третьем классе Test реализованы два статических метода Test_ClassObject() и TestClassGen(). В этих методах нарочно выполняется ошибочная операция присваивания числу objInt строки objString

objInt = objString;

здесь

  • objInt – объект типа Integer (число);
  • objString – объект типа String (строка).

В методе Test_ClassGen() эта ошибочная операция обнаруживается на этапе компиляции. То есть, выполнение программы не состоится, пока эта ошибка не будет исправлена. В этом случае срабатывает механизм типовой безопасности.
В методе Test_ClassObject() ошибка обнаруживается на этапе выполнения (этап компиляции не определяет ошибку). То есть механизм типовой безопасности не срабатывает. А это недопустимо.

public class Test {
  // Метод, который тестирует класс, в котором объявляется ссылка типа Object
  public static void Test_ClassObject() {
    // Создать экземпляр типа Integer и присвоить переменной типа int его значение
    ClassObject objInt = new ClassObject(25);
    int vInt = (Integer)objInt.GetValue(); // здесь нужно явное приведение типов

    // Создать экземпляр типа String
    ClassObject objString = new ClassObject("Hello");
    String vString = (String)objString.GetValue(); // здесь также нужно явное приведение типов

    // Присвоить переменной типа Integer значение переменной типа String
    objInt = objString; // здесь компилятор ошибки не выдает - это неправильно
    int value = (Integer)objInt.GetValue(); // ошибка на этапе выполнения!!!
  }

  // Класс, использующий механизм обобщений
  public static void Test_ClassGen() {

    // Создать экземпляр типа Integer
    ClassGen<Integer> objInt = new ClassGen<Integer>(25);
    int vInt = objInt.GetValue(); // здесь не нужно явное приведение типов

    // Создать экземпляр типа String
    ClassGen<String> objString = new ClassGen<String>("Hello");
    String vString = objString.GetValue(); // здесь также не нужно явное приведение типов

    // Присвоить типу Integer значение типа String
    objInt = objString; // ошибка на этапе компиляции - срабатывает типовая безопасность
  }

  public static void main(String[] args) {
    // Вызов методов тестирования
    Test_ClassObject();
    Test_ClassGen();
  }
}

 

2. Обобщенный интерфейс. Особенности использования

В языке Java допускается объявлять интерфейс, который использует один или несколько обобщенных типов. Общая форма объявления обобщенного интерфейса с именем InterfaceName, который использует тип T, следующая:

interface InterfaceName<T> {
  ...
}

Общая форма обобщенного интерфейса, который использует несколько обобщенных типов, следующая:

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

здесь

  • InterfaceName – имя обобщенного интерфейса, который использует типы T1, T2, …, TN.

 

3. Пример объявления и использования обобщенного интерфейса с одним параметром типа

В примере объявляется один обобщенный интерфейс IValue<T> и обобщенный класс Value<T>. Интерфейс и класс получают параметром тип T. В интерфейсе объявляется метод Show(), реализуемый в классе. Класс содержит внутреннюю переменную value.

В классе GenInterface реализованы две статических функции, демонстрирующие использование интерфейса IValue<T> и класса Value<T>.

// Интерфейс, использующий один параметр типа T
interface IValue<T> {

  // Методы доступа к значению
  T GetValue();
  void SetValue(T value);

  // Метод Show() - выводит информацию о значении
  void Show();
}

// Обобщенный класс, реализующий интерфейс IValue<T>
class Value<T> implements IValue<T> {

  // Внутренние переменные класса
  T value;

  // Конструктор
  public Value(T _value) {
    value = _value;
  }

  // Реализация методов интерфейса
  public T GetValue() {
    return value;
  }

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

  // Реализация метода Show() интерфейса
  public void Show() {
    System.out.println("value = " + value);
  }
}

public class GenInterface {

  // Метод, получающий ссылку на интерфейс IValue
  public static void ShowValue(IValue ref) {
    ref.Show();
  }

  public static void main(String[] args) {
    // 1. Объявить ссылку на интерфейс IValue<T>
    IValue<Double> refV;

    // 2. Создать экземпляр класса Value<T>
    Value<Double> obj = new Value<Double>(5.88);

    // 3. Проверка метода GetValue() класса Value<T>
    double t = obj.GetValue();
    System.out.println("t = " + t); // t = 5.88

    // 4. Проверка метода SetValue()
    obj.SetValue(7.777);
    t = obj.GetValue();
    System.out.println("t = " + t); // t = 7.777

    // 5. Проверка метода Show()
    obj.Show(); // value = 7.777

    // 6. Вызов статического метода ShowValue() с параметром ссылки на интерфейс refV
    refV = obj; // Присвоить ссылке адрес экземпляра класса Value<T>
    ShowValue(refV); // value = 7.777

    // 7. Вызов статического метода ShowValue() с параметром экземпляра obj
    ShowValue(obj); // value = 7.777
  }
}

Результат выполнения программы

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

 

4. Простой пример объявления обобщенного интерфейса и его использование в обобщенном классе, который использует 3 параметра типа T1, T2, T3

Данный пример есть демонстрацией объявления и использования обобщенных интерфейсов в языке Java.

В примере продемонстрировано использование обобщенного интерфейса с именем MyInterface. Интерфейс объявляет методы, которые оперируют тремя типами T1, T2, T3. Также объявляется класс MyClass<T1, T2, T3>, реализующий интерфейс MyInterface<T1, T2, T3>. В классе объявляются три внутренние переменные, которые имеют соответственно типы T1, T2, T3.

// Интерфейс, использующий 3 обобщенных типа
interface MyInterface<T1, T2, T3> {
  // В интерфейсе указываются методы, которые используют типы T1, T2, T3
  // Вернуть значение переменных типов T1, T2, T3
  T1 GetT1();
  T2 GetT2();
  T3 GetT3();

  // Установить значения переменных типов T1, T2, T3
  void SetT1(T1 obj);
  void SetT2(T2 obj);
  void SetT3(T3 obj);

  // Метод вывода значений переменных
  void Print(String text);
}

// Класс, реализующий интерфейс MyInterface<T1, T2, T3>.
// В объявлении класса обязательно нужно указывать имена типов T1, T2, T3.
class MyClass<T1, T2, T3> implements MyInterface<T1, T2, T3> {
  // Внутренние переменные класса
  T1 var1;
  T2 var2;
  T3 var3;

  // Конструктор класса
  public MyClass(T1 var1, T2 var2, T3 var3) {
    this.var1 = var1;
    this.var2 = var2;
    this.var3 = var3;
  }

  // Методы, которые объявлены в интерфейсе 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) {

    // Демонстрация использования обобщенного интерфейса MyInterface<T1, T2, T3>

    // 1. Объявить ссылку на интерфейс, который использует типы Integer, Character и Long
    MyInterface<Integer, Character, Long> ref;

    // 2. Объявить экземпляр класса MyClass, который обрабатывает типы Integer, Character, Long
    MyClass<Integer, Character, Long> obj = new MyClass<Integer, Character, Long>(25, 'z', 3223232332L);

    // 3. Получить значение типа Integer (int) - метод GetT1()
    int vInt = obj.GetT1();
    System.out.println("vInt = " + vInt);

    // 4. Получить значение типа Character (char) - метод GetT2()
    char vChar1 = obj.GetT2();
    Character vChar2 = obj.GetT2(); // так тоже можно
    System.out.println("vChar1 = " + vChar1);
    System.out.println("vChar2 = " + vChar2);

    // 5. Получить значение типа Long (long) - метод GetT3()
    long vLong;
    vLong = obj.GetT3();
    System.out.println("vLong = " + vLong);

    // 6. Вывести все внутренние переменные - метод Print()
    obj.Print("-----------------");
  }
}

Результат выполнения программы

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

 


Связанные темы