TypeScript에서 추상 메서드를 올바르게 정의하는 방법을 찾으려고합니다.
원래 상속 예제 사용 :
class Animal {
constructor(public name) { }
makeSound(input : string) : string;
move(meters) {
alert(this.name + " moved " + meters + "m.");
}
}
class Snake extends Animal {
constructor(name) { super(name); }
makeSound(input : string) : string {
return "sssss"+input;
}
move() {
alert("Slithering...");
super.move(5);
}
}
메소드 makeSound를 올바르게 정의하는 방법을 알고 싶습니다. 그래서 입력하고 덮어 쓸 수 있습니다.
또한 올바른 protected
방법 을 정의하는 방법을 잘 모르겠습니다 . 키워드 인 것처럼 보이지만 효과가 없으며 코드가 컴파일되지 않습니다.
답변
name
속성은 다음과 같이 표시된다 protected
. 이것은 TypeScript 1.3에 추가되었으며 이제 확실하게 설정되었습니다.
makeSound
방법으로 표시된 abstract
클래스처럼. Animal
지금은 추상적이기 때문에 직접 인스턴스화 할 수 없습니다 . 이것은 TypeScript 1.6의 일부 이며 현재 공식적으로 제공됩니다.
abstract class Animal {
constructor(protected name: string) { }
abstract makeSound(input : string) : string;
move(meters) {
alert(this.name + " moved " + meters + "m.");
}
}
class Snake extends Animal {
constructor(name: string) { super(name); }
makeSound(input : string) : string {
return "sssss"+input;
}
move() {
alert("Slithering...");
super.move(5);
}
}
추상적 인 방법을 모방 한 오래된 방법은 누군가가 그것을 사용하면 오류를 던졌습니다. TypeScript 1.6이 프로젝트에 도달하면 더 이상이 작업을 수행 할 필요가 없습니다.
class Animal {
constructor(public name) { }
makeSound(input : string) : string {
throw new Error('This method is abstract');
}
move(meters) {
alert(this.name + " moved " + meters + "m.");
}
}
class Snake extends Animal {
constructor(name) { super(name); }
makeSound(input : string) : string {
return "sssss"+input;
}
move() {
alert("Slithering...");
super.move(5);
}
}
답변
Erics가 조금 더 대답하면 다형성을 완벽하게 지원하고 기본 클래스에서 구현 된 메소드를 호출 할 수있는 추상 클래스의 꽤 괜찮은 구현을 실제로 만들 수 있습니다. 코드로 시작하자 :
/**
* The interface defines all abstract methods and extends the concrete base class
*/
interface IAnimal extends Animal {
speak() : void;
}
/**
* The abstract base class only defines concrete methods & properties.
*/
class Animal {
private _impl : IAnimal;
public name : string;
/**
* Here comes the clever part: by letting the constructor take an
* implementation of IAnimal as argument Animal cannot be instantiated
* without a valid implementation of the abstract methods.
*/
constructor(impl : IAnimal, name : string) {
this.name = name;
this._impl = impl;
// The `impl` object can be used to delegate functionality to the
// implementation class.
console.log(this.name + " is born!");
this._impl.speak();
}
}
class Dog extends Animal implements IAnimal {
constructor(name : string) {
// The child class simply passes itself to Animal
super(this, name);
}
public speak() {
console.log("bark");
}
}
var dog = new Dog("Bob");
dog.speak(); //logs "bark"
console.log(dog instanceof Dog); //true
console.log(dog instanceof Animal); //true
console.log(dog.name); //"Bob"
Animal
클래스는 구현이 필요하기 때문에 추상 메소드의 유효한 구현 없이는 IAnimal
유형의 오브젝트를 구성 할 수 Animal
없습니다. 다형성이 작동하려면가 IAnimal
아닌 인스턴스를 전달해야합니다 Animal
. 예 :
//This works
function letTheIAnimalSpeak(animal: IAnimal) {
console.log(animal.name + " says:");
animal.speak();
}
//This doesn't ("The property 'speak' does not exist on value of type 'Animal')
function letTheAnimalSpeak(animal: Animal) {
console.log(animal.name + " says:");
animal.speak();
}
Erics의 대답과의 주요 차이점은 “추상적 인”기본 클래스 는 인터페이스의 구현이 필요 하므로 자체적으로 인스턴스화 할 수 없다는 것입니다.
답변
인터페이스와 기본 클래스의 조합을 사용하면 효과가 있다고 생각합니다. 컴파일 타임에 행동 요구 사항을 시행합니다 (rq_ post “아래”는 위의 게시물을 의미합니다).
인터페이스는 기본 클래스가 충족하지 않는 동작 API를 설정합니다. 인터페이스에 정의 된 메소드를 호출하도록 기본 클래스 메소드를 설정할 수 없습니다 (이러한 동작을 정의하지 않고 기본 클래스에서 해당 인터페이스를 구현할 수 없기 때문에). 어쩌면 누군가 부모의 인터페이스 메소드 호출을 허용 하는 안전한 트릭을 만들 수 있습니다.
인스턴스화 할 클래스에서 확장하고 구현해야합니다. 런타임 실패 코드 정의에 대한 우려를 만족시킵니다. 또한 인터페이스를 구현하지 않은 경우 (예 : Animal 클래스를 인스턴스화하려는 경우) 발생하는 메소드를 호출 할 수도 없습니다. 인터페이스를 아래 BaseAnimal로 확장하려고 시도했지만 Snake에서 BaseAnimal의 생성자 및 ‘name’필드를 숨겼습니다. 내가 그렇게 할 수 있다면, 모듈과 내보내기를 사용하면 우연히 BaseAnimal 클래스를 직접 인스턴스화하지 못하게 될 수 있습니다.
여기에 붙여 넣어 자신에게 적합한 지 확인하십시오. http://www.typescriptlang.org/Playground/
// The behavioral interface also needs to extend base for substitutability
interface AbstractAnimal extends BaseAnimal {
// encapsulates animal behaviors that must be implemented
makeSound(input : string): string;
}
class BaseAnimal {
constructor(public name) { }
move(meters) {
alert(this.name + " moved " + meters + "m.");
}
}
// If concrete class doesn't extend both, it cannot use super methods.
class Snake extends BaseAnimal implements AbstractAnimal {
constructor(name) { super(name); }
makeSound(input : string): string {
var utterance = "sssss"+input;
alert(utterance);
return utterance;
}
move() {
alert("Slithering...");
super.move(5);
}
}
var longMover = new Snake("windy man");
longMover.makeSound("...am I nothing?");
longMover.move();
var fulture = new BaseAnimal("bob fossil");
// compile error on makeSound() because it is not defined.
// fulture.makeSound("you know, like a...")
fulture.move(1);
아래 링크 된 FristvanCampen의 답변을 발견했습니다. 그는 추상 클래스는 반 패턴이며, 하나는 구현 클래스의 주입 된 인스턴스를 사용하여 기본 ‘추상’클래스를 인스턴스화 할 것을 제안합니다. 이것은 공정하지만 반대 주장이 있습니다. 스스로 읽어보십시오 :
https://typescript.codeplex.com/discussions/449920
2 부 : 추상 클래스를 원했던 또 다른 경우가 있었지만 “추상 클래스”의 정의 된 메소드가 일치하는 인터페이스에 정의 된 메소드를 참조해야하기 때문에 위의 솔루션을 사용하지 못했습니다. 그래서 FristvanCampen의 조언을 도구로 사용합니다. 메소드 구현과 함께 불완전한 “추상”클래스가 있습니다. 구현되지 않은 메소드와의 인터페이스가 있습니다. 이 인터페이스는 “추상”클래스를 확장합니다. 그런 다음 첫 번째를 확장하고 두 번째를 구현하는 클래스가 있습니다 (그렇지 않으면 수퍼 생성자가 액세스 할 수 없으므로 둘 다 확장해야합니다). 아래의 (실행 불가능한) 샘플을 참조하십시오.
export class OntologyConceptFilter extends FilterWidget.FilterWidget<ConceptGraph.Node, ConceptGraph.Link> implements FilterWidget.IFilterWidget<ConceptGraph.Node, ConceptGraph.Link> {
subMenuTitle = "Ontologies Rendered"; // overload or overshadow?
constructor(
public conceptGraph: ConceptGraph.ConceptGraph,
graphView: PathToRoot.ConceptPathsToRoot,
implementation: FilterWidget.IFilterWidget<ConceptGraph.Node, ConceptGraph.Link>
){
super(graphView);
this.implementation = this;
}
}
과
export class FilterWidget<N extends GraphView.BaseNode, L extends GraphView.BaseLink<GraphView.BaseNode>> {
public implementation: IFilterWidget<N, L>
filterContainer: JQuery;
public subMenuTitle : string; // Given value in children
constructor(
public graphView: GraphView.GraphView<N, L>
){
}
doStuff(node: N){
this.implementation.generateStuff(thing);
}
}
export interface IFilterWidget<N extends GraphView.BaseNode, L extends GraphView.BaseLink<GraphView.BaseNode>> extends FilterWidget<N, L> {
generateStuff(node: N): string;
}
답변
기본 클래스에서 예외를 던지는 데 사용합니다.
protected abstractMethod() {
throw new Error("abstractMethod not implemented");
}
그런 다음 하위 클래스에서 구현해야합니다. 단점은 빌드 오류가 없지만 런타임이라는 것입니다. 장점은 작동한다고 가정하고 수퍼 클래스 에서이 메소드를 호출 할 수 있다는 것입니다. 🙂
HTH!
밀턴
답변
아니, 아니! 언어가 해당 기능을 지원하지 않을 때 자신 만의 ‘추상’클래스와 메소드를 만들려고 시도하지 마십시오. 주어진 언어가 지원되기를 원하는 언어 기능도 마찬가지입니다. TypeScript에서 추상 메서드를 구현하는 올바른 방법은 없습니다. 특정 클래스가 직접 인스턴스화되지는 않지만 명시 적 으로이 금지 사항을 적용하지 않도록 이름 지정 규칙으로 코드를 구성하십시오.
또한 위의 예제는 Java / C #에서 예상 한 것처럼 컴파일 타임이 아닌 런타임에만이 적용을 제공합니다.