Objective-C中的子类化和继承

3
我有一个叫做“AbstractBook”的类,它有一个属性:
@property(strong) AbstractPage *page;

现在假设我有一个名为AbstractBook的子类,名为WhiteBook,以及一个名为AbstractPage的子类,名为WhitePage。我希望WhiteBook类继承page对象,但是在这个类中,我希望它是特定的WhitePage类,而不是AbstractPage
因此,在WhiteBook中,我只需将属性重新声明为WhitePage即可:
@property(strong) WhitePage *page;

然而,我收到了一个警告:WhitePage与从AbstractBook继承的AbstractPage不兼容。实现我想要的正确方法是什么?

4个回答

5
你想要做的事情在根本上是不可能的。不要忘记这一点:
@property(strong) AbstractPage *page;

声明了一个getter和一个setter。

一般来说(这适用于面向对象编程,不仅仅是Objective-C),重写的getter返回超类实现返回类型的子类是没有问题的,因为这不会破坏API契约。在你的情况下,WhitePage的实例也是AbstractPage的实例。

然而,setter是不同的。你不能限制参数的类型,因为你的子类必须能够在任何使用AbstractBook的地方使用,所以调用在它认为是AbstractBook上的setter的代码有权传入任何AbstractPage子类的实例,因为这就是API所说的。

我认为我会添加一个名为的方法到AbstractBook中,例如

-(bool) isLegalPage: (AbstractPage) aPage;

在基类中,该方法总是返回true,但在WhiteBook中只有白色页面才返回true。然后我会手动实现setter方法如下:
-(void) setPage: (AbstractPage*) aPage
{
    if (![self isLegalPage: aPage])
    {
        // throw an exception or do other error notification
    }
    else
    {
        Do the assignment according to reference count/ARC/GC model
    }
}

如果页面不合法,设置器将抛出异常,人们应该使用isLegalPage:方法来测试。

另一种选择是在基类和子类中使用不同的设置器拥有只读属性,或者在基类中根本没有设置器。


3

Objective C 中没有 协变 属性类型,因此如果您想要一个返回 AbstractPage 子类的属性,您需要在 WhiteBook 子类中定义一个单独的属性,比如说 whitePage。您也可以从您的 page 属性中返回相同的值,这也可以工作。此外,如果您的用户在 AbstractPage* 上调用方法,他们甚至不需要将其转换为 WhitePage*


在我的子类中创建一个名为whitePage的新属性,并实现一个自定义的getter方法,该方法返回超类对象。 - Snowman
@mohabitar 是的,那将是一个解决方案。如果基类是抽象的,不要合成“page”属性:这将让每个book子类使用其页面子类的正确类型的ivar。 - Sergey Kalinichenko
等等,抽象类中不合成?那我最初该如何设置它?我不想设置特定的whitePage属性,我只想设置AbstractPage页面,而getter返回特定类型。 - Snowman
@mohabitar,你绝对可以重用抽象类中的合成属性,但是如果你想从ivar访问页面的属性,那么你需要在代码中进行类型转换,或者使用自动进行类型转换的属性。 - Sergey Kalinichenko

2

记住要基于接口而非实现进行编程。

======================

选项:1

如果你的 Whitebook 真的是 AbstractBook 的后代,那么你不需要让 WhiteBook 拥有一个名为“page”的新属性,因为你的 AbstractBook 已经声明了它,所以 WhiteBook 会自动继承它。

只要你的 WhitePage 继承自 AbstractPage,你就应该能够将你的页面对象强制转换为 WhitePage。然后你就可以获得 AbstractPage 的所有原始功能,以及 WhitePage 的功能。

因此,你不需要在 Whitebook 中重新声明 page。如果你去你的 Whitebook.h 文件并删除这一行:

@property(strong) WhitePage *page;

接下来进入您的Whitebook.m文件并键入self.page,您不应该收到任何编译警告。然后,您可以将page转换为WhitePage,然后就可以继续进行了。

警告:这意味着您需要在使用page时将其强制转换为WhitePage,这并不理想。

======================

选项2

您也可以尝试将代码保留原样,但确保在WhiteBook中重新合成“page”属性。

======================

选项3

您也可以尝试将代码保留原样,但确保在WhiteBook中使用@dynamic为“page”属性。然后自己实现getter并返回WhitePage而不是AbstractPage。


我想我会选择选项3。不过,在抽象类中将属性合成,然后在子类中使其动态化,这样可以吗? - Snowman
选项3将极其超出常规。 - bbum
真的吗?那哪个是标准呢?我可以使用选项2,但警告会一直存在,对吧? - Snowman
要么创建第二个更具体的属性,要么使用返回类型(id)(并向Objective-C提交错误报告,请求协变支持)。 - bbum

0

如何使用更具体的类型覆盖超类属性?

通常情况下,如果您在AbstractBook类中使用@synthesize,并在WhiteBook类中使用@dynamic,那么它应该可以正常工作(除非您使用AbstractPage合成方法将非WhitePage分配给page)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接