una tipos superclase que metodo llamar herencia hacer ejemplos como clase objective-c oop inheritance initialization

objective-c - tipos - superclase java



Inicializando una clase usando el inicializador de superclase (1)

Generalmente en Objective-C crea un inicializador designado para cada clase y luego las subclases usan el mismo inicializador. Entonces, en lugar de usar initAnimal e initDog, simplemente usas init. La subclase dog definiría entonces su propio método init y llamaría al inicializador designado en su clase principal:

@implementation Dog -(id)init { if( (self = [super init]) ) { // call init in Animal and assign to self // do something specific to a dog } return self; } @end

Realmente no tiene que especificar initDog e initAnimal porque la clase está declarada en el lado derecho de la tarea ...

Actualización: agrego lo siguiente a la respuesta para reflejar la información adicional en la pregunta

Hay varias maneras de garantizar que las subclases no llamen a inicializadores que no sean el inicializador designado, y la forma en que elija en última instancia se basará principalmente en su diseño completo. Una de las cosas buenas de Objective-C es que es muy flexible. Te daré dos ejemplos aquí para que comiences.

Primero, si crea una subclase que tiene un inicializador diferente designado que su clase principal, puede sobrecargar el inicializador de los padres y lanzar una excepción. Esto permitirá que los programadores sepan de inmediato que han violado el protocolo para su clase ... sin embargo, se debe establecer que usted debe tener una muy buena razón para hacerlo y que debe estar muy bien documentado que la subclase puede no usar el mismo inicializador que la superclase.

@implementation Dog -(id)init { // Dog does not respond to this initializer NSAssert( false, @"Dog classes must use one of the designated initializers; see the documentation for more information." ); [self autorelease]; return nil; } -(id)initWithFur:(FurOptionsType)furOptions { if( (self = [super init]) ) { // do stuff and set up the fur based on the options } return self; } @end

Otra forma de hacerlo es tener un inicializador más parecido al ejemplo original. En ese caso, puede cambiar el init predeterminado en la clase principal para que siempre falle. Luego puede crear un inicializador privado para su clase principal y luego asegurarse de que todos llamen al inicializador apropiado en las subclases. Este caso es obviamente más complicado:

@interface Animal : NSObject -(id)initAnimal; @end @interface Animal () -(id)_prvInitAnimal; @end @interface Dog : Animal -(id)initDog; @end @implementation Animal -(id)init { NSAssert( false, @"Objects must call designated initializers; see documentation for details." ); [self autorelease]; return nil; } -(id)initAnimal { NSAssert( [self isMemberOfClass:[Animal class]], @"Only Animal may call initAnimal" ); // core animal initialization done in private initializer return [self _prvInitAnimal]; } -(id)_prvInitAnimal { if( (self = [super init]) ) { // do standard animal initialization } return self; } @end @implementation Dog -(id)initDog { if( (self = [super _prvInitAnimal]) ) { // do some dog related stuff } return self; } @end

Aquí puede ver la interfaz y la implementación de la clase Animal y Dog. El Animal es el objeto de nivel superior designado y, por lo tanto, anula la implementación de init de NSObject. Cualquier persona que llame a init en una subclase de Animal o cualquiera de los animales recibirá un error de aserción refiriéndose a la documentación. Animal también define un inicializador privado en una categoría privada. La categoría privada permanecería con su código y las subclases de Animal llamarían a este inicializador privado cuando llamen a super. Su propósito es llamar a init en la superclase de Animal (NSObject en este caso) y hacer cualquier inicialización genérica que pueda ser necesaria.

Finalmente, la primera línea en el método initAnimal de Animal es una afirmación de que el receptor es realmente un Animal y no una subclase. Si el receptor no es un Animal, el programa fallará con un error de aserción y el programador será referido a la documentación.

Estos son solo dos ejemplos de cómo puede diseñar algo con sus requisitos específicos. Sin embargo, le sugiero que considere sus limitaciones de diseño y vea si realmente necesita este tipo de diseño, ya que no es estándar en Cocoa y en la mayoría de los marcos de diseño de OO. Por ejemplo, puede considerar hacer varios objetos a nivel de raíz de animales y simplemente tener un protocolo de Animal en su lugar, requiriendo que todos los diversos "animales" respondan a ciertos mensajes genéricos de animales. De esta forma, cada animal (y las verdaderas subclases de Animal) pueden manejar ellos mismos sus inicializadores designados y no tendrán que depender de que las superclases se comporten de una manera tan específica, no estándar.

Tengo dos clases, una subclase de la otra (por ejemplo, Animal y Dog ). La superclase tiene algunos inicializadores (digamos initAnimal ), la subclase tiene algunos inicializadores (digamos initDog ). El problema es que es perfectamente legal (desde el punto de vista del compilador) hacer algo como Dog *adog = [[Dog alloc] initAnimal] , es decir. inicializa una clase usando su inicializador de superclase. No me gusta esto, porque la subclase puede tener algunas variables de instancia adicionales que quiero asegurarme de que se hayan inicializado. Una mirada al archivo de encabezado resuelve esto, pero ¿hay alguna manera simple de hacer que el compilador verifique por mí? Tengo la sensación de que me estoy perdiendo algo terriblemente obvio, pero no puedo entenderlo :-)

Actualización: el initDog y initAnimal no fueron los mejores ejemplos. initWithFur decir dos inicializadores realmente diferentes (como init para Animal e initWithFur para Dog ). Si quisiera que cada perro tuviera un pelaje asignado, habría hecho que el pelaje formara parte del inicializador, de modo que nadie pudiera obtener un objeto de perro sin un pelaje. Pero aún es fácil inicializar erróneamente la instancia con la superclase init , y luego me manchan.

Gracias por mencionar los inicializadores designados, Jason. No se me había ocurrido antes, pero podría sobrecargar el inicializador designado de la superclase y establecer allí algunos valores predeterminados sanos. Pero preferiría si de alguna manera pudiera hacer que fuera ilegal usar otros inicializadores que los de la misma clase: ¿más ideas?