我想创建一个带有类型参数的抽象类,并且该类的构造函数应该传递另一个动作,例如。
abstract class Action<Tc> {
public function __construct(private ?Action<*> $onSuccess = null) {}
}
如何表达类型参数通配符,即。“?” (Java) 还是 Hack 中的 "_" (Scala)?
Hack 现在没有通配符类型参数,所以你能得到的最接近的实际上是指定一个你实际上不需要的虚拟类型参数,例如,
abstract class Action<Tc, Ta> {
public function __construct(private ?Action<Ta> $onSuccess = null) {}
// ...
}
根据您使用$onSuccess
成员变量的准确程度,您可能希望它是Action<T>
稍后确定的某个特定子类,因此您可能想要这样的东西:
abstract class Action<Tc, Ta, To as Action<Ta>> {
public function __construct(private ?To $onSuccess = null) {}
// ...
}
但是,我质疑上面的“虚拟”类型是否真的是虚拟的——绝大多数用例Action<T>
都会关心它到底T
是什么,否则你将如何使用Action<T>
? (当然很少有你不关心T
调用站点的情况,但它们很少见,所以我鼓励你在构建这个功能时考虑这是否真的是你的情况。)
不确定通配符,但这可以实现您想要的吗?
<?hh
abstract class Action<T1 as Action, T2> {
public function __construct(private ?T1 $onSuccess = null, private ?T2 $bla = null) {}
}
class ActionA<T1 as Action, T2> extends Action<T1, T2> {}
class ActionB<T1 as Action, T2> extends Action<T1, T2> {}
class ActionC<T1 as Action, T2> extends Action<T1, T2> {}
$action = new ActionA(new ActionB(new ActionC(null)));
var_dump($action);
当我对 HHVM 3.1.0 运行它时,我得到:
object(ActionA)#1 (2) {
["onSuccess":"Action":private]=>
object(ActionB)#2 (2) {
["onSuccess":"Action":private]=>
object(ActionC)#3 (2) {
["onSuccess":"Action":private]=>
NULL
["bla":"Action":private]=>
NULL
}
["bla":"Action":private]=>
NULL
}
["bla":"Action":private]=>
NULL
}
并且 3.1.0 类型检查器也返回“没有错误!”。
但是,T1 as Action
关于抽象类的声明似乎没有强制执行。例如,我可以将实例化行更改为:
$action = new ActionA(new ActionB(new ActionC(new DateTime())));
它嗡嗡作响,类型检查器仍然没有返回错误。这是在使用<?hh // strict
.
所以不是你的答案,但也许很接近?上述行为可能表明 Hack 对这种模式存在一些问题?