简体   繁体   中英

Is there a way to make a subclass not respond to a method that its superclass implements, and have the compiler throw an error?

Suppose I have a class called 'Node' that has a method 'addChild'

@interface Node : NSObject

-(void) addChild:(Node *)n;

and I have a subclass called 'Sprite' that shouldn't respond to this method.

@interface Sprite : Node

I know I could do something like this:

@implementation Sprite

-(void) addChild:(Node *)n {
    NSLog(@"Don't call addChild on a Sprite!");
}

or

-(void) addChild:(Node *)n {
    NSAssert(NO, @"Don't call addChild on a Sprite!");
}

But is there a way to declare that the subclass doesn't respond to this method, and have the compiler throw an error? Getting a compiler error would be a lot better than a runtime error in this case.

EDIT

I realize this violates the Liskov substitution principle. But in Apple's documentation there's this:

"Any doesNotRecognizeSelector: messages are generally sent only by the runtime system. However, they can be used in program code to prevent a method from being inherited. For example, an NSObject subclass might renounce the copy or init method by re-implementing it to include a doesNotRecognizeSelector: message..."

I don't understand why throwing a runtime error rather than a compile-time error would be any less in violation of this principle.

No.

There is a principle in OOP called the Liskov substitution principle which states that you must always be able to substitute an instance of a subclass for an instance of the original class without altering the desired behaviour of the program in question. In your case, you desire that Nodes should respond to addChild: ; in order to obey the LSP we must have Sprites respond to addChild: too.

That's not to say that there's nothing you can do. You could have Sprites throw an exception when you send addChild: to them, or have them silently ignore it, but I think that what you actually want is for Node and Sprite to be different subclasses of the same parent - let's call it AbstractNode. You'd move most of the logic from Node into AbstractNode, then just implement addChild: on Node.

I'm not sure about a compile time error, but during runtime you can try:

-(void)myMethod {
    [self doesNotRecognizeSelector:_cmd];
}

However, I think you need to refactor these classes where you want to effectively eliminate a method from the parent class. It seems that you're violating inheritance otherwise.

You can use a category to extend the class and implement your addChild method in that category.

@interface Node(firstCategory)
-(void)addChild:(Node *)n;
@end

@implementation Node(firstCategory)
// your implementation of addChild
@end

If you just want to have it respond to the super class (In this case 'Node'). Just not implement it on your subclass 'Sprite' or manually call it inside your method. If your want to do something extra.

@implementation Sprite

-(void) addChild:(Node *)n {
    [super addChild:n];
    // Do some other stuff 
}

If your want your subclass to ignore the method completely you have to implement the method and do nothing, but if this is the case you've misunderstood the inheritance paradigm.

@implementation Sprite

-(void) addChild:(Node *)n {
     // Ignore the method
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM