В чем разница между "расширяет" и "реализует" в TypeScript
Я хотел бы знать, что общего между мужчиной и ребенком и чем они отличаются.
class Person {
name: string;
age: number;
}
class child extends Person {}
class man implements Person {}
7 ответов
Укороченная версия
extends
средства:
Новый класс - ребенок. Это получает выгоду от наследства. У него есть все свойства, методы в качестве его родителя. Он может переопределить некоторые из них и реализовать новое, но родительский материал уже включен.
implements
средства:
Новый класс можно рассматривать как одну и ту же "форму", пока он не дочерний. Это может быть передано любому методу, где Person
требуется, независимо от наличия другого родителя, чем Person
Больше...
В ООП (языки как C#, Java) мы использовали бы
extends
получить прибыль от наследства (см. вики). Небольшая ссылка:
... Наследование в большинстве объектно-ориентированных языков на основе классов - это механизм, при котором один объект приобретает все свойства и поведение родительского объекта. Наследование позволяет программистам: создавать классы, основанные на существующих классах...
implements
будет больше для полиморфизма (см. вики). Небольшая ссылка:
... полиморфизм - это предоставление единого интерфейса сущностям разных типов...
Таким образом, мы можем иметь действительно другое дерево наследования нашего class Man
,
class Man extends Human ...
но если мы также объявим, что мы можем притвориться, что другой тип - Person
:
class Man extends Human
implements Person ...
.. тогда мы можем использовать его где угодно, где Person
необходимо. Мы просто должны выполнить Персонал "interface"
(т.е. реализовать все свои публичные вещи).
implement
другой класс? Это действительно классная штука
Javascript "приятное лицо" (одно из преимуществ) - встроенная поддержка печати по утке ( см. Вики). Небольшая ссылка:
"Если он ходит как утка и крякает как утка, значит, это утка".
Так, в Javascript, если два разных объекта... будет иметь один похожий метод (например, render()
) они могут быть переданы функции, которая ожидает этого:
function(engine){
engine.render() // any type implementing render() can be passed
}
Чтобы не потерять это - мы можем в Typescript сделать то же самое - с более типизированной поддержкой. И это где
class implements class
имеет свою роль, где это имеет смысл
На ООП языках как C#
... нет способа сделать это...
Также документация должна помочь здесь:
Интерфейсы, расширяющие классы
Когда тип интерфейса расширяет тип класса, он наследует членов класса, но не их реализации. Это как если бы интерфейс объявил всех членов класса без предоставления реализации. Интерфейсы наследуют даже частные и защищенные члены базового класса. Это означает, что когда вы создаете интерфейс, который расширяет класс частными или защищенными членами, этот тип интерфейса может быть реализован только этим классом или его подклассом.
Это полезно, когда у вас большая иерархия наследования, но вы хотите указать, что ваш код работает только с подклассами, которые имеют определенные свойства. Подклассы не должны быть связаны кроме наследования от базового класса. Например:
class Control { private state: any; } interface SelectableControl extends Control { select(): void; } class Button extends Control { select() { } } class TextBox extends Control { select() { } } class Image extends Control { } class Location { select() { } }
Так что пока
extends
значит - он получает все от своего родителяimplements
в этом случае это почти как реализация интерфейса. Дочерний объект может притвориться, что он является родителем... но он не получает никакой реализации
В машинописи (и некоторых других языках OO) у вас есть классы и интерфейсы.
Интерфейс не имеет реализации, это просто "контракт" на то, какие члены / методы имеют этот тип.
Например:
interface Point {
x: number;
y: number;
distance(other: Point): number;
}
Экземпляры, которые реализуют это Point
Интерфейс должен иметь два члена типа номер: x
а также y
и один метод distance
который получает другой Point
экземпляр и возвращает number
,
Интерфейс не реализует ничего из этого.
Классы являются реализациями:
class PointImplementation implements Point {
public x: number;
public y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
public distance(other: Point): number {
return Math.sqrt(Math.pow(this.x - other.x, 2) + Math.pow(this.y - other.y, 2));
}
}
В вашем примере вы относитесь к Person
Класс один раз как класс, когда вы расширяете его, и один раз как интерфейс, когда вы реализуете его.
Ваш код:
class Person {
name: string;
age: number;
}
class Child extends Person {}
class Man implements Person {}
Ошибка компиляции:
Класс "Человек" неправильно реализует интерфейс "Человек".
Свойство name отсутствует в типе Man.
И это потому, что интерфейсы не имеют реализации.
Так что если вы implement
класс, тогда вы только берете его "контракт" без реализации, поэтому вам нужно сделать это:
class NoErrorMan implements Person {
name: string;
age: number;
}
Суть в том, что в большинстве случаев вы хотите extend
другой класс и не implement
Это.
Расширяет инструменты VS
extends
: Дочерний класс (который расширяется) наследует все свойства и методы класса, который расширяется.implements
: Класс, который используетimplements
ключевое слово потребуется для реализации всех свойств и методов класса, который онimplements
Проще говоря:
extends
: Здесь вы получаете все эти методы / свойства из родительского класса, поэтому вам не нужно реализовывать это самостоятельноimplements
: Вот договор, которому должен следовать класс. Класс должен реализовывать как минимум следующие методы / свойства
Пример:
class Person {
name: string;
age: number;
walk(): void {
console.log('Walking (person Class)')
}
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
class child extends Person { }
// Man has to implements at least all the properties
// and methods of the Person class
class man implements Person {
name: string;
age: number
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
walk(): void {
console.log('Walking (man class)')
}
}
(new child('Mike', 12)).walk();
// logs: Walking(person Class)
(new man('Tom', 12)).walk();
// logs: Walking(man class)
В этом примере мы можем заметить, что дочерний класс наследует все от Person, тогда как класс man должен реализовать все от самого Person.
Если бы мы удалили что-то из класса man, например метод walk, мы получили бы следующую ошибку времени компиляции:
Класс man неправильно реализует класс Person. Вы хотели расширить "Person" и унаследовать его члены как подкласс? Свойство walk отсутствует в типе man, но обязательно в типе Person.(2720)
Отличный ответ от @nitzan-tomer! Мне очень помогло... Я немного расширил его демо:
IPoint interface;
Point implements IPoint;
Point3D extends Point;
И как они ведут себя в функциях, ожидающих тип IPoint.
Итак, что я выучил до сих пор и использовал как правило большого пальца: если вы используете классы и методы, ожидающие универсальные типы, используйте интерфейсы в качестве ожидаемых типов. И убедитесь, что родительский или базовый класс использует этот интерфейс. Таким образом, вы можете использовать все подклассы в тех, которые реализуют интерфейс.
По сути:
-
extends
получит все свойства и методы родительского класса. -
implements
обяжет нас реализовать все свойства и методы, определенные в интерфейсе.
- Интерфейс расширяет интерфейс с помощью формы
- Интерфейс расширяет класс формой
- Класс реализует интерфейс должен реализовывать все поля, предоставляемые интерфейсом
- Класс реализует класс с формой
- Класс расширяет класс всеми полями
extends
сосредоточиться на наследовать и implements
сосредоточиться на ограничении, будь то интерфейсы или классы.
Интерфейсы помогают уменьшить связь между классами, предоставляя контракт или общий язык, который определяет, как разные классы могут взаимодействовать друг с другом, обеспечивая слабую связь и облегчая обслуживание и расширяемость (программирование для интерфейса). Использование интерфейсов вместо наследования сводит к минимуму зависимости между классами, что приводит к уменьшению связанности кода. Такое разделение повышает возможность повторного использования кода, тестируемость и общую гибкость системы.