JavaRush /Курсы /Модуль 2: Fullstack /Закрепляем работу с дженериками

Закрепляем работу с дженериками

Модуль 2: Fullstack
6 уровень , 3 лекция
Открыта

4.1 Обобщенные классы

Обобщенные классы (они же Generic classes) позволяют нам создавать классы, которые могут работать с различными типами данных, сохраняя при этом типовую безопасность.

Синтаксис обобщенных классов

Обобщенные классы объявляются с использованием угловых скобок (<>) для указания параметров типов.

Пример:

JavaScript
    
      "use strict";
      class Box {
          constructor(contents) {
              this.contents = contents;
          }
          getContents() {
              return this.contents;
          }
          setContents(contents) {
              this.contents = contents;
          }
      }
      let stringBox = new Box("Hello");
      console.log(stringBox.getContents()); // Вывод: Hello
      let numberBox = new Box(42);
      console.log(numberBox.getContents()); // Вывод: 42
    
  
TypeScript
    
      class Box<T> {
        private contents: T;

        constructor(contents: T) {
          this.contents = contents;
        }

        getContents(): T {
          return this.contents;
        }

        setContents(contents: T): void {
          this.contents = contents;
        }
      }

      let stringBox = new Box<string>("Hello");
      console.log(stringBox.getContents()); // Вывод: Hello

      let numberBox = new Box<number>(42);
      console.log(numberBox.getContents()); // Вывод: 42
    
  

Здесь класс Box использует обобщенный тип T для определения типа содержимого. Это позволяет создать экземпляры Box для различных типов данных, таких как строки и числа.

Примеры обобщенных классов

Стек (Stack)

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

Пример:

JavaScript
    
      "use strict";
      class Stack {
          constructor() {
              this.items = [];
          }
          push(item) {
              this.items.push(item);
          }
          pop() {
              return this.items.pop();
          }
          peek() {
              return this.items[this.items.length - 1];
          }
          isEmpty() {
              return this.items.length === 0;
          }
      }
      let numberStack = new Stack();
      numberStack.push(10);
      numberStack.push(20);
      console.log(numberStack.peek()); // Вывод: 20
      console.log(numberStack.pop()); // Вывод: 20
      console.log(numberStack.isEmpty()); // Вывод: false
      let stringStack = new Stack();
      stringStack.push("A");
      stringStack.push("B");
      console.log(stringStack.peek()); // Вывод: B
      console.log(stringStack.pop()); // Вывод: B
      console.log(stringStack.isEmpty()); // Вывод: false
    
  
TypeScript
    
      class Stack<T> {
        private items: T[] = [];

        push(item: T): void {
          this.items.push(item);
        }

        pop(): T | undefined {
          return this.items.pop();
        }

        peek(): T | undefined {
          return this.items[this.items.length - 1];
        }

        isEmpty(): boolean {
          return this.items.length === 0;
        }
      }

      let numberStack = new Stack<number>();
      numberStack.push(10);
      numberStack.push(20);
      console.log(numberStack.peek()); // Вывод: 20
      console.log(numberStack.pop()); // Вывод: 20
      console.log(numberStack.isEmpty()); // Вывод: false

      let stringStack = new Stack<string>();
      stringStack.push("A");
      stringStack.push("B");
      console.log(stringStack.peek()); // Вывод: B
      console.log(stringStack.pop()); // Вывод: B
      console.log(stringStack.isEmpty()); // Вывод: false
    
  

Здесь класс Stack является обобщенным и может использоваться для создания стеков для различных типов данных, таких как числа и строки.

4.2 Обобщенные интерфейсы

Обобщенные интерфейсы (Generic interfaces) позволяют нам определять интерфейсы, которые могут работать с различными типами данных. Это полезно для описания структур данных, которые должны быть универсальными и типобезопасными.

Синтаксис обобщенных интерфейсов

Обобщенные интерфейсы объявляются с использованием угловых скобок (<>) для указания параметров типов.

Пример:

JavaScript
    
      "use strict";
      let pair = {
          first: "Age",
          second: 30
      };
      console.log(pair.first); // Вывод: Age
      console.log(pair.second); // Вывод: 30
    
  
TypeScript
    
      interface Pair<T, U> {
        first: T;
        second: U;
      }

      let pair: Pair<string, number> = {
        first: "Age",
        second: 30
      };

      console.log(pair.first); // Вывод: Age
      console.log(pair.second); // Вывод: 30
    
  

Здесь интерфейс Pair принимает два параметра типа T и U, которые определяют типы его свойств — first и second.

Примеры обобщенных интерфейсов

Кортеж (Tuple)

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

Пример:

JavaScript
    
      "use strict";
      let stringNumberTuple = {
          first: "Hello",
          second: 42
      };
      let booleanStringTuple = {
          first: true,
          second: "World"
      };
      console.log(stringNumberTuple); // Вывод: { first: 'Hello', second: 42 }
      console.log(booleanStringTuple); // Вывод: { first: true, second: 'World' }
    
  
TypeScript
    
      interface Tuple<T, U> {
        first: T;
        second: U;
      }

      let stringNumberTuple: Tuple<string, number> = {
        first: "Hello",
        second: 42
      };

      let booleanStringTuple: Tuple<boolean, string> = {
        first: true,
        second: "World"
      };

      console.log(stringNumberTuple); // Вывод: { first: 'Hello', second: 42 }
      console.log(booleanStringTuple); // Вывод: { first: true, second: 'World' }
    
  

Здесь интерфейс Tuple принимает два параметра типа T и U, которые определяют типы его свойств — first и second.

4.3 Реальные примеры

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

Пример 1: Реализация словаря

JavaScript
    
      "use strict";
      class GenericDictionary {
          constructor() {
              this.items = {};
          }
          setItem(key, value) {
              this.items[key] = value;
          }
          getItem(key) {
              return this.items[key];
          }
          removeItem(key) {
              delete this.items[key];
          }
          getKeys() {
              return Object.keys(this.items);
          }
      }
      let numberDictionary = new GenericDictionary();
      numberDictionary.setItem("one", 1);
      numberDictionary.setItem("two", 2);
      console.log(numberDictionary.getItem("one")); // Вывод: 1
      numberDictionary.removeItem("one");
      console.log(numberDictionary.getKeys()); // Вывод: ['two']
    
  
TypeScript
    
      interface Dictionary<T> {
        [key: string]: T;
      }

      class GenericDictionary<T> implements Dictionary<T> {
        private items: Dictionary<T> = {};

        setItem(key: string, value: T): void {
          this.items[key] = value;
        }

        getItem(key: string): T | undefined {
          return this.items[key];
        }

        removeItem(key: string): void {
          delete this.items[key];
        }

        getKeys(): string[] {
          return Object.keys(this.items);
        }
      }

      let numberDictionary = new GenericDictionary<number>();
      numberDictionary.setItem("one", 1);
      numberDictionary.setItem("two", 2);
      console.log(numberDictionary.getItem("one")); // Вывод: 1
      numberDictionary.removeItem("one");
      console.log(numberDictionary.getKeys()); // Вывод: ['two']
    
  

Здесь класс GenericDictionary реализует интерфейс Dictionary и использует обобщенный тип T для определения типа значений в словаре.

Пример 2: Реализация связанного списка

JavaScript
    
      "use strict";
      class ListNode {
          constructor(value) {
              this.next = null;
              this.value = value;
          }
      }
      class LinkedList {
          constructor() {
              this.head = null;
          }
          add(value) {
              const newNode = new ListNode(value);
              if (this.head === null) {
                  this.head = newNode;
              }
              else {
                  let current = this.head;
                  while (current.next !== null) {
                      current = current.next;
                  }
                  current.next = newNode;
              }
          }
          remove(value) {
              if (this.head === null) {
                  return false;
              }
              if (this.head.value === value) {
                  this.head = this.head.next;
                  return true;
              }
              let current = this.head;
              while (current.next !== null && current.next.value !== value) {
                  current = current.next;
              }
              if (current.next === null) {
                  return false;
              }
              current.next = current.next.next;
              return true;
          }
          print() {
              let current = this.head;
              while (current !== null) {
                  console.log(current.value);
                  current = current.next;
              }
          }
      }
      let list = new LinkedList();
      list.add(1);
      list.add(2);
      list.add(3);
      list.print(); // Вывод: 1, 2, 3
      list.remove(2);
      list.print(); // Вывод: 1, 3
    
  
TypeScript
    
      class ListNode<T> {
        value: T;
        next: ListNode<T> | null = null;

        constructor(value: T) {
          this.value = value;
        }
      }

      class LinkedList<T> {
        private head: ListNode<T> | null = null;

        add(value: T): void {
          const newNode = new ListNode(value);
          if (this.head === null) {
            this.head = newNode;
          } else {
            let current = this.head;
            while (current.next !== null) {
              current = current.next;
            }
            current.next = newNode;
          }
        }

        remove(value: T): boolean {
          if (this.head === null) {
            return false;
          }

          if (this.head.value === value) {
            this.head = this.head.next;
            return true;
          }

          let current = this.head;
          while (current.next !== null && current.next.value !== value) {
            current = current.next;
          }

          if (current.next === null) {
            return false;
          }

          current.next = current.next.next;
          return true;
        }

        print(): void {
          let current = this.head;
          while (current !== null) {
            console.log(current.value);
            current = current.next;
          }
        }
      }

      let list = new LinkedList<number>();
      list.add(1);
      list.add(2);
      list.add(3);
      list.print(); // Вывод: 1, 2, 3
      list.remove(2);
      list.print(); // Вывод: 1, 3
    
  

Здесь классы ListNode и LinkedList используют обобщенные типы для определения типа элементов в связанном списке.

3
Задача
Модуль 2: Fullstack, 6 уровень, 3 лекция
Недоступна
Реализация обобщенного класса Box
Реализация обобщенного класса Box
3
Задача
Модуль 2: Fullstack, 6 уровень, 3 лекция
Недоступна
Реализация обобщенного класса Stack
Реализация обобщенного класса Stack
3
Задача
Модуль 2: Fullstack, 6 уровень, 3 лекция
Недоступна
Реализация обобщенного интерфейса Pair
Реализация обобщенного интерфейса Pair
3
Задача
Модуль 2: Fullstack, 6 уровень, 3 лекция
Недоступна
Реализация обобщенного словаря **
Реализация обобщенного словаря **
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ