Jaka jest różnica między „rozszerza” a „implementuje” w języku TypeScript

Odpowiedzi:

155

Krótka wersja

  • extends znaczy:

Nowa klasa jest dzieckiem . Otrzymuje korzyści wynikające z dziedziczenia. Ma wszystkie właściwości, metody jako jego rodzic. Może zastąpić niektóre z nich i zaimplementować nowe, ale elementy nadrzędne są już uwzględnione.

  • implements znaczy:

Nowej klasy mogą być traktowane jako ten sam „kształtu” , a to nie jest dziecko . Można go przekazać do dowolnej metody, w której Personjest wymagana, niezależnie od tego, czy ma innego rodzica niżPerson

Więcej ...

W OOP (języki takie jak C #, Java) używalibyśmy

extendsczerpać korzyści z dziedziczenia (patrz wiki ). Mały cytat:

... Dziedziczenie w większości języków obiektowych opartych na klasach jest mechanizmem, w którym jeden obiekt przejmuje wszystkie właściwości i zachowania obiektu nadrzędnego. Dziedziczenie umożliwia programistom: tworzenie klas, które są zbudowane na podstawie istniejących klas ...

implementsbędzie bardziej dla polimorfizmu (patrz wiki ). Mały cytat:

... polimorfizm to zapewnienie jednego interfejsu dla bytów różnych typów ...

Więc możemy mieć naprawdę inne drzewo dziedziczenia class Man.

class Man extends Human ...

ale jeśli również zadeklarujemy, że możemy udawać innego typu - Person:

class Man extends Human 
          implements Person ...

.. wtedy możemy go używać wszędzie tam, gdzie Personjest to wymagane. Musimy tylko spełnić Persons "interface" (tj. Wdrożyć wszystkie jej publiczne rzeczy) .

implementinna klasa? To naprawdę fajna rzecz

Miła twarz Javascript (jedną z zalet) to wbudowana obsługa pisania typu Duck ( zobacz wiki ). Mały cytat:

„Jeśli chodzi jak kaczka i kwacze jak kaczka, to musi to być kaczka”.

Tak więc, w Javascript, jeśli dwa różne obiekty ... miałyby jedną podobną metodę (np. render()) , Można je przekazać do funkcji, która tego oczekuje:

function(engine){
  engine.render() // any type implementing render() can be passed
}

Aby tego nie stracić - możemy zrobić to samo w Typescript - z większą obsługą pisania. I to jest gdzie

class implements class

ma swoją rolę tam, gdzie ma to sens

W językach OOP jako C#... nie da się tego zrobić ...

Również dokumentacja powinna tutaj pomóc:

Interfejsy rozszerzające klasy

Gdy typ interfejsu rozszerza typ klasy, dziedziczy elementy członkowskie klasy, ale nie ich implementacje. To tak, jakby interfejs zadeklarował wszystkie elementy członkowskie klasy bez dostarczania implementacji. Interfejsy dziedziczą nawet prywatne i chronione elementy członkowskie klasy bazowej. Oznacza to, że kiedy tworzysz interfejs, który rozszerza klasę o prywatne lub chronione elementy członkowskie, ten typ interfejsu może być implementowany tylko przez tę klasę lub jej podklasę.

Jest to przydatne, gdy masz dużą hierarchię dziedziczenia, ale chcesz określić, że kod działa tylko z podklasami, które mają określone właściwości. Podklasy nie muszą być powiązane poza dziedziczeniem po klasie bazowej. Na przykład:

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 {

}

Więc gdy

  • extends oznacza - dostaje wszystko od swojego rodzica
  • implementsw tym przypadku jest to prawie jak implementacja interfejsu. Obiekt potomny może udawać, że jest rodzicem ... ale nie otrzymuje żadnej implementacji
Radim Köhler
źródło
kiedy mówisz „ extends-dostaje wszystko od swojego rodzica”, czy dotyczy to prywatnych członków? Na przykład class Person {private name: string} class man extends Person{gender: string;}czy manma nazwę nieruchomości?
davejoem
Prywatne też tam są. Po prostu niedostępny dla TS. Zabezpiecz je i możesz z nich korzystać. W przypadku „narzędzi” sens ma tylko część publiczna. Mam nadzieję, że to trochę pomoże
Radim Köhler
Doskonała odpowiedź. Tylko nie jestem pewien, czy Twój komentarz dotyczący „prywatnego jest tam, ale nie jest dostępny dla TS”. Czy masz na myśli, że prywatne właściwości są kopiowane w nowo utworzonym obiekcie potomnym? A w przypadku narzędzi kopiowane są tylko właściwości publiczne?
kushalvm
Omówiłem też jeszcze jeden punkt. Jeśli taka jest definicja extends. W takim razie proszę, czy mógłbyś wyjaśnić ten stackoverflow.com/questions/60390454/ ...
kushalvm
99

W maszynopisie (i niektórych innych językach obiektowych) masz klasy i interfejsy.

Interfejs nie ma implementacji, jest po prostu „kontraktem” elementów członkowskich / metody tego typu.
Na przykład:

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

Instancje, które implementują ten Pointinterfejs, muszą mieć dwa elementy członkowskie typu numer: xi yoraz jedną metodę, distancektóra odbiera inne Pointwystąpienie i zwraca plik number.
Interfejs nie implementuje żadnego z nich.

Zajęcia to implementacje:

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));
    }
}

( kod na placu zabaw )

W twoim przykładzie traktujesz swoją Personklasę raz jako klasę, kiedy ją rozszerzasz, a raz jako interfejs, kiedy ją implementujesz.
Twój kod:

class Person {
    name: string;
    age: number;
}
class Child  extends Person {}

class Man implements Person {}

Występuje błąd kompilacji mówiący:

Klasa „Man” nieprawidłowo implementuje interfejs „Person”.
Brak „nazwy” właściwości w typie „Man”.

A to dlatego, że interfejsy nie są implementowane.
Więc jeśli jesteś implementklasą, bierzesz tylko jej „kontrakt” bez implementacji, więc musisz zrobić to:

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

( kod na placu zabaw )

Najważniejsze jest to, że w większości przypadków chcesz iść do extendinnej klasy, a nie do implementniej.

Nitzan Tomer
źródło
6

Świetna odpowiedź od @ nitzan-tomer! Bardzo mi pomogło ... Rozszerzyłem nieco jego demo o:

IPoint interface;
Point implements IPoint;
Point3D extends Point;

I jak zachowują się w funkcjach oczekujących IPointtypu.

Więc czego się do tej pory nauczyłem i używam jako reguły kciuka: jeśli używasz klas i metod oczekujących typów ogólnych, używaj interfejsów jako oczekiwanych typów. I upewnij się, że klasa nadrzędna lub klasa bazowa używa tego interfejsu. W ten sposób możesz użyć wszystkich podklas w tych, o ile implementują interfejs.

Tutaj rozszerzone demo

andzep
źródło
To nie daje odpowiedzi na pytanie. Aby skrytykować lub poprosić autora o wyjaśnienie, zostaw komentarz pod jego postem. - Z recenzji
aronisstav
1
@aronisstav Opublikowałem tylko rozszerzone demo, na które znalazłem dobrą odpowiedź, która już mi pomogła. Ale może ktoś inny uznałby pracę, którą wykonałem, rozszerzając wersję demonstracyjną, za przydatną. To wszystko. Komentarze nie są tak naprawdę przeznaczone do umieszczania bloków kodu, dlatego uważam je za bardziej zrozumiałe w poście odpowiedzi. Więc jaki jest z tym problem?
andzep,
Twoja odpowiedź została (automatycznie?) Oznaczona ze względu na długość i treść, pojawiła się w mojej kolejce recenzji, a ja uzasadniłem przyczyny przedstawione na fladze. Jego główny wkład (wyjaśniający, że rozszerzyłeś demo) byłby lepszy jako komentarz. Z dodanym akapitem być może rzeczywiście jest to bardziej przydatne.
aronisstav
@andzep Twój rozszerzony przykład demo jest naprawdę pomocny.
namit
3
  1. Interfejs rozszerza interfejs o kształt
  2. Interfejs rozszerza klasę o kształt
  3. Interfejs implementuje klasę powinien implementować wszystkie pola udostępnione przez interfejs
  4. Klasa implementuje klasę z kształtem
  5. Klasa rozszerza klasę o wszystkie pola

extendsskoncentruj się na dziedziczeniu i implementsskup się na ograniczeniach, czy to interfejsach, czy klasach.

lei li
źródło
0

Rozszerza narzędzia VS.

  • extends: Klasa potomna (która jest rozszerzona) odziedziczy wszystkie właściwości i metody klasy jest extends
  • implements: Klasa, która używa implementssłowa kluczowego, będzie musiała zaimplementować wszystkie właściwości i metody klasy, w której jest używaneimplements

Mówiąc prościej:

  • extends: Tutaj otrzymujesz wszystkie te metody / właściwości z klasy nadrzędnej, więc nie musisz ich implementować samodzielnie
  • implements: Oto kontrakt, którego klasa musi przestrzegać. Klasa musi implementować co najmniej następujące metody / właściwości

Przykład:

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)

W tym przykładzie możemy zauważyć, że klasa potomna dziedziczy wszystko po Person, podczas gdy klasa man musi zaimplementować wszystko od samej Osoby.

Gdybyśmy usunęli coś z klasy man, na przykład metodę walk, otrzymalibyśmy następujący błąd czasu kompilacji :

Klasa „man” nieprawidłowo implementuje klasę „Person”. Czy chodziło Ci o rozszerzenie „Person” i dziedziczenie jego członków jako podklasy? Brakuje właściwości „spacer” w typie „mężczyzna”, ale jest ona wymagana w typie „Osoba”. (2720)

Willem van der Veen
źródło