4.1 Обобщенные классы
Обобщенные классы (они же Generic classes) позволяют нам создавать классы, которые могут работать с различными типами данных, сохраняя при этом типовую безопасность.
Синтаксис обобщенных классов
Обобщенные классы объявляются с использованием угловых скобок (<>) для указания параметров типов.
Пример:
"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
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)
Обобщенные классы особенно полезны для создания коллекций, таких как стеки, которые могут работать с любыми типами данных.
Пример:
"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
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) позволяют нам определять интерфейсы, которые могут работать с различными типами данных. Это полезно для описания структур данных, которые должны быть универсальными и типобезопасными.
Синтаксис обобщенных интерфейсов
Обобщенные интерфейсы объявляются с использованием угловых скобок (<>) для указания параметров типов.
Пример:
"use strict";
let pair = {
first: "Age",
second: 30
};
console.log(pair.first); // Вывод: Age
console.log(pair.second); // Вывод: 30
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)
Обобщенные интерфейсы полезны для описания кортежей, которые могут содержать элементы различных типов.
Пример:
"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' }
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: Реализация словаря
"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']
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: Реализация связанного списка
"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
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 используют обобщенные типы для определения типа элементов в связанном списке.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ