Яка різниця між "extends" і "реализаторами" в TypeScript


126

Я хотів би знати, що спільного між чоловіком і дитиною і чим вони відрізняються.

class Person {
  name: string;
  age: number;
}
class child extends Person {}
class man implements Person {}

1
Чудово пояснено тут: stackoverflow.com/a/35990799/452213 . В Зокрема, приклад: typescriptlang.org/play / ...
Sudip

Відповіді:


154

Коротка версія

  • extends засоби:

Новий клас є дитиною . Він отримує вигоди, що надходять із спадщиною. Він має всі властивості, методи як його батьківський. Він може змінити деякі з них і реалізувати нові, але материнські речі вже включені.

  • implements засоби:

До нового класу можна ставитися як до тієї ж «форми» , поки це не дитина . Він може бути переданий до будь-якого методу, де Personнеобхідний, незалежно від того, чи є інший батьківський, ніжPerson

Більше ...

У OOP (такі мови, як C #, Java) ми б використовували

extendsотримати прибуток від спадщини (див. wiki ). Маленький цитат:

... Спадкування в більшості класових об'єктно-орієнтованих мов - це механізм, за допомогою якого один об'єкт набуває всіх властивостей і поведінки батьківського об'єкта. Спадщина дозволяє програмістам: створювати класи, побудовані на існуючих класах ...

implementsбуде більше для поліморфізму (див. wiki ). Маленький цитат:

... поліморфізм - це надання єдиного інтерфейсу суб'єктам різних типів ...

Отже, у нас може бути справді інше дерево спадкування 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

має свою роль, де це має сенс

У мовах OOP як C#... немає ніякого способу зробити це ...

Також тут може допомогти документація:

Розширення класів інтерфейсів

Коли тип інтерфейсу розширює тип класу, він успадковує членів класу, але не їх реалізацію. Наче інтерфейс оголосив усіх членів класу, не надаючи реалізації. Інтерфейси успадковують навіть приватні та захищені члени базового класу. Це означає, що коли ви створюєте інтерфейс, який розширює клас із приватними або захищеними членами, цей тип інтерфейсу може бути реалізований лише тим класом або його підкласом.

Це корисно, коли у вас є велика ієрархія спадкування, але ви хочете вказати, що ваш код працює лише з підкласами, які мають певні властивості. Підкласи не повинні бути пов’язані, крім успадкування від базового класу. Наприклад:

class Control {
    private state: any;
}

interface SelectableControl extends Control {
    select(): void;
}

class Button extends Control implements SelectableControl {
    select() { }
}

class TextBox extends Control {
    select() { }
}

// Error: Property 'state' is missing in type 'Image'.
class Image implements SelectableControl {
    private state: any;
    select() { }
}

class Location {

}

Отже, поки

  • extends означає - отримує все від свого батька
  • implementsу цьому випадку майже як реалізація інтерфейсу. Дочірній об’єкт може зробити вигляд, що він є батьківським ... але він не отримує жодної реалізації

коли ти кажеш, що " extendsотримує все від батьків", це стосується приватних членів? Наприклад, class Person {private name: string} class man extends Person{gender: string;}чи manє ім'я властивості?
davejoem

Приватні також є. Просто недоступний для TS. Зробіть їх захищеними, і ви можете ними користуватися. У випадку "втілення в життя" просто публічна частина має сенс. Сподіваюсь, це трохи допоможе
Радім Келер

Відмінна відповідь. Просто не впевнений у вашому коментарі до "приватне є, але TS не доступне". Ви маєте на увазі, що приватні властивості копіюються в новостворений дочірній об’єкт? А у випадку знарядь копіюються лише загальнодоступні властивості?
kushalvm

Також я обійшов ще одну точку. Якщо це визначення розширень. Тоді , будь ласка , якщо ви могли б пояснити це stackoverflow.com/questions/60390454 / ...
kushalvm

98

У машинописі (та деяких інших мовах ОО) у вас є класи та інтерфейси.

Інтерфейс не має реалізації, це просто "договір" про те, які члени / метод має цей тип.
Наприклад:

interface Point {
    x: number;
    y: number;
    distance(other: Point): number;
}

Примірники, які реалізують цей Pointінтерфейс, повинні мати два члени типу типу: xі yта один метод, distanceякий отримує інший Pointекземпляр і повертає a 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 {}

Помилка компіляції:

Клас 'Man' неправильно реалізує інтерфейс 'Person'.
Властивості 'name' відсутнє в типі 'Man'.

І це тому, що в інтерфейсах бракує реалізації.
Тож якщо ви implementклас, то ви приймаєте лише його "контракт" без реалізації, тому вам потрібно буде це зробити:

class NoErrorMan implements Person {
    name: string;
    age: number;
}

( код на ігровому майданчику )

Підсумок полягає в тому, що в більшості випадків ви хочете до extendіншого класу, а не до implementнього.


7
Ця відповідь простіша для розуміння.
Акшай Раут

6

Чудовий відповідь від @ nitzan-tomer! Мені дуже допомогли ... Я трохи продовжив його демонстрацію за допомогою:

IPoint interface;
Point implements IPoint;
Point3D extends Point;

І як вони поводяться у функціях, що очікують IPointтипу.

Тож, про що я дізнався до цих пір, і я використовую як правило: якщо ви використовуєте класи та методи, що очікують загальні типи, використовуйте інтерфейси як очікувані типи. І переконайтеся, що батьківський чи базовий клас використовує цей інтерфейс. Таким чином, ви можете використовувати всі підкласи в тих, наскільки вони реалізують інтерфейс.

Тут розширена демонстрація


Це не дає відповіді на запитання. Щоб критикувати або вимагати роз'яснення у автора, залиште коментар під їх дописом. - З огляду
aronisstav

1
@aronisstav Я лише розмістив розширену демонстрацію того, що знайшов гарну відповідь, що мені вже допомогло. Але, можливо, хтось інший знайде корисну роботу, яку я зробив, розширюючи демонстрацію. Це все. Коментарі насправді не призначені для розміщення кодового блоку, тому я вважаю, що це зрозуміліше у відповіді. То в чому проблема?
andzep

Ваша відповідь (автоматично?) Позначена через довжину та вміст, з’явилася у черзі моїх оглядів, і я заслужив за причини, подані у прапорі. Його основний внесок (пояснюючи, що ви розширили демонстрацію) було б краще, як коментар. З доданим абзацом, можливо, це справді корисніше.
aronisstav

@andzep ваш розширений демонстраційний приклад дуже корисний.
НАМИТ

3
  1. Інтерфейс розширює інтерфейс з формою
  2. Інтерфейс розширює клас з формою
  3. Інтерфейс класових програм повинен реалізувати всі поля, передбачені інтерфейсом
  4. Клас реалізує клас з формою
  5. Клас розширює клас на всі поля

extendsзосередити увагу на успадкуванні та implementsзосередитись на обмеженні, будь то інтерфейси чи класи.


0

Розширює реалізацію 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, тоді як клас людини повинен реалізовувати все від Person.

Якби ми видаляли щось із класу man, наприклад метод walk, ми отримали б таку помилку часу компіляції :

Клас 'man' неправильно реалізує клас 'Person'. Ви мали намір поширити "Особу" та успадкувати її членів як підклас? Властивість "прогулянка" відсутня в типі "людина", але вимагається в типі "людина" (2720)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.