【问题标题】:Why does MATLAB attempt to instantiate abstract properties?为什么 MATLAB 会尝试实例化抽象属性?
【发布时间】:2021-02-12 07:58:57
【问题描述】:

在 MATLAB 中将类属性验证与抽象属性结合使用时会出现(对我而言)意外行为。这是一个涉及三个类的简短示例:

classdef TestClass
    properties (Abstract)
        aprop (1,1) TestClassB
    end
end

classdef TestClassB
    methods
        function obj = TestClassB(a)
            disp(a);
        end
    end
end

classdef TestClassC < TestClass
    properties
        aprop
    end
end

然后,当尝试实例化 TestClassC 对象时,我收到以下错误:

>> TestClassC()
Error defining property 'aprop' of class 'TestClass'. Unable to construct default
object of class TestClassB.

显然,MATLAB 正在尝试在 TestClass 中实例化抽象属性 aprop,然后由于 aprop 的类定义 TestClassB 中缺少有效的零参数构造函数而失败。我意识到零参数构造函数问题在理论上是可以修复的(尽管只有当您有权修改 TestClassB 时),但 MATLAB 正在实例化一个抽象属性这一事实似乎很奇怪。维基百科关于抽象类型的文章的第一行字面意思是:

...抽象类型是主格类型系统中不能直接实例化的类型...

也许这是 MATLAB 作为弱类型语言实现属性验证的唯一方式,但这是一个不幸的结果。

附带说明,如果 TestClassB 有一个有效的零参数构造函数但被定义为抽象的,那么您最终会落入同一个陷阱,但会出现稍微不同的错误:

>> TestClassC()
Error defining property 'aprop' of class 'TestClass'. Class TestClassB is abstract.
Specify a default value for property aprop.

这种行为有充分的理由吗?

编辑:

额外的测试类观察

  1. 删除TestClass 中的大小验证可消除错误。显然,MATLAB 在没有有效的零参数构造函数的情况下初始化一个空对象数组是没有问题的。注意:这仅适用于 TestClassB抽象的。
  2. 如果TestClassB 是抽象的并且TestClassCTestClassB 的具体子类在属性块中初始化aprop,则没有错误(假设具体子类具有有效的零参数构造函数)。注意:如果apropTestClassC 的构造函数中被初始化,这为真。

激励例子

上面的UML图是一个典型的继承与组合结合的例子。 GenericCarGenericEngine 是抽象类。 GenericCar 包含 GenericEngine,但 Racecar 将其引擎指定为具体的 V8(其中“isa”GenericEngine)。不幸的是,这不能在 MATLAB 中使用属性验证来构造,因为 MATLAB 不允许您重新定义抽象属性的类,即使它是原始定义的子类。

假设您接受不能将Racecar 中的engine 定义为V8。即便如此,如果您没有为engine 提供初始值Racecar 的属性块 中,MATLAB 在创建 Racecar 对象时会抛出错误,因为它会尝试初始化 @987654353 @ 使用抽象类型GenericEngine。即使Racecar 的构造函数将引擎初始化为V8 对象也是如此。 这突出了属性验证和对象实例化之间的奇怪联系。

【问题讨论】:

  • “这突出了属性验证和对象实例化之间的奇怪联系。”但我认为这是有道理的。如果一个属性不能是[],但必须是GenericEngine,那么当对象被实例化时,它必须有一个GenericEngine作为属性。否则 MATLAB 应该做什么?如果属性验证是您所追求的,为什么不 (1) 制作一个验证值的 setter,或者 (2) 将属性设为只读,在构造函数中设置并且永不更改?
  • 顺便说一下,您在此处尝试执行的继承类型在 MATLAB 中毫无意义。 MATLAB 不是强类型语言,除了继承行为之外不需要使用继承(检查他们如何实现句柄基类和类似类)。您可以更简单地构建代码,不需要 engine 是抽象类 GenericEngine 的子类,但要求它具有特定的行为(它实现某些功能)。
  • PS:要验证一个对象在您的 setter 函数中是否具有所需的方法,请使用 ismethod
  • @CrisLuengo 老实说,我不确定 MATLAB 应该做什么。也许将属性留空并仅在实际设置属性时应用验证?当然,这也需要权衡取舍,例如用不严格有效的属性实例化一个对象。或者,它至少可以在构造函数执行之前推迟验证属性,但这可能也会产生副作用。至于变通方法,我同意还有其他可能不太“直接”的方法来实现相同或相似的结果,但是属性验证的意义何在?
  • @CrisLuengo 您提出了一些关于如何在 MATLAB 等弱类型语言中最好地使用 OOP 的有趣观点。你能指出我提到的关于句柄基类的信息吗?至于要求特定行为而不是使用抽象类,这不正是抽象类的用途,定义了必须实现的方法吗?似乎手动检查对象中的方法名称是实现相同功能的一种迂回方式。

标签: matlab validation oop abstract abstract-data-type


【解决方案1】:

看来应该回答两个问题:

1 - “显然,MATLAB 正在尝试在 TestClass 中实例化抽象属性 aprop。那么,为什么 MATLAB 会尝试实例化抽象属性?”

根据documentation(重点是我):

您可以为抽象属性定义属性验证。验证适用于实现该属性的所有子类

MATLAB 正在尝试在TestClassC 中实例化属性aprop,它是TestClass具体 子类。但它使用与TestClass 相同的验证规则。 TestClassCaprop 属性不是抽象的。

2 - “如果 TestClassB ... 被定义为抽象的,你最终会落入同一个陷阱...”

参考documentation

抽象类 — 不能实例化的类,但它定义了子类使用的类组件。

TestClassC 使用与TestClass 相同的验证规则。它尝试实例化其 aprop 成员,该成员属于 TestClassB 类,但 TestClassB 是抽象的,无法实例化。

【讨论】:

  • 感谢您的跟进。我想我应该更清楚地了解 MATLAB 在做什么,但我发现 MATLAB 的实现消除了抽象属性的大多数富有成效的用例。抽象属性的要点是成为子类中具体定义的占位符。因此,永远不需要实例化超类中的定义。基本上,MATLAB 将属性验证视为“预构造函数”,强制在对象创建时实例化属性,而不管“抽象性”如何。这在文档中不是很清楚。
  • 我希望您有兴趣了解什么是 MATLAB 行为,并尝试解释该行为,并与您和可能的其他用户分享我的发现。我喜欢你相信有很多地方需要改进文档。如果您认为这是您想要的,请接受答案。当您编辑了问题时,看来我也应该编辑我的答案并扩展可能基于意见的讨论。如果您还有其他问题,我认为最好将它们作为新帖子发送。
猜你喜欢
  • 2019-02-06
  • 2019-09-07
  • 2012-02-27
  • 1970-01-01
  • 2013-05-05
  • 1970-01-01
  • 2014-07-12
  • 2018-09-10
  • 1970-01-01
相关资源
最近更新 更多