我可以在类型提示中允许两种不同的类型吗?
例如,参数$requester
可以是User
或File
中的任意一种:
function log (User|File $requester) {
}
我可以在类型提示中允许两种不同的类型吗?
例如,参数$requester
可以是User
或File
中的任意一种:
function log (User|File $requester) {
}
提案以61票赞成、5票反对的结果获得通过,并且已经准备好实施。
有一个之前的RFC也曾提出过类似的想法,正如另一个答案中提到的那样,但是那个提案最终被否决了。
它将与您问题中的示例完全相同:
class F
{
public function foo (File|Resource $f) : int|float { /** implement this**// }
}
这意味着F :: foo()
的期望值是File
或资源,并将返回int
或float
。null
声明一个union。 A|null
等价于?A
,但更复杂的声明A | B | null
也是可能的。
false
类型也是可能的。例如:int | false
。这主要源于历史原因,因为某些内部函数在某些类型的错误条件下返回false
。请参见strpos()。null
或引发异常,但是包括此选项以考虑遗留代码。
F
类,这是合法的:class G extends F
{
public function foo(File|Resource|string $f) : int { /** **/ }
}
但是这并不是:
class H extends F
{
public function foo(File $f) : int|float|bool { /** **/ }
}
myMethod(): void|MyException
不起作用。 - Adamvoid
或者MyException
的实例”。没有“可能抛出异常”的语法,您需要使用注释或属性来标注。 - yivi学术上,这被称为联合类型。
你可以通过创建接口、父类型等方式来欺骗,正如其他答案中提到的那样,但这有什么意义呢,除了增加复杂性和项目行数?此外,对于标量类型,这是行不通的,因为你无法扩展/实现标量类型。
与其使代码更易读,倒不如相反。除非那些类/接口已经存在并且它们在这里是因为面向对象编程而不是为了解决类型提示问题。
在 PHP 中,规范的做法就是... 不要放置类型提示。该语言并没有考虑具有复杂且强大的类型系统,试图绕过语言的缺陷并不是一个好答案。
相反,请正确记录您的函数:
/**
* Description of what the function does.
*
* @param User|File $multiTypeArgument Description of the argument.
*
* @return string[] Description of the function's return value.
*/
function myFunction($multiTypeArgument)
{
这至少可以为自动完成和静态代码分析带来IDE支持,在处理私人项目、网站等方面非常有用。
当设计公共API(例如PHP库等)时,有时您可能希望更加谨慎地处理API消费者的输入。
那么,@tilz0R的答案就是正确的选择:
function log($message) {
if (!is_string($message) && !$message instanceof Message) {
throw new \InvalidArgumentException('$message must be a string or a Message object.');
}
// code ...
}
2015年2月14日,Union Types PHP RFC 被提议用于 PHP 7.1。经过讨论和投票,它被否决了,18票反对,11票赞成。
如果这个RFC被接受了,PHP将会有像你展示的一样的联合类型(User|File
)。
这个RFC有一些缺陷,但主要原因是 维护者 投票人对于变化特别是涉及到类型严格性和其他编程范例(例如:"为什么我们需要类型联合,当默认可以接受所有类型的值" 和 "那会影响性能")持有相当大的抵抗心理。
目前在 PHP 中无法实现。但是你可以拥有一个interface
,并为User
和File
实现它,然后在log()
中使用该接口作为类型提示:
<?php
interface UserFile {
}
class User implements UserFile {
}
class File implements UserFile {
}
// snip
public function log (UserFile $requester) {
}
您可以在函数内部检查类型。
function log ($requester) {
if ($requester instanceof User || $requester instanceof File) {
//Do your job
}
}
或者您可以为每个方法都设置2个,以及一个动态方法:
function logUser($requester)
{
//
}
function logFile($requester)
{
//
}
还有动态的
function log($requester)
{
if ($requester instanceof File) {
return $this->logFile($requester);
}
if ($requester instanceof User) {
return $this->logUser($requester);
}
throw new LogMethodException();
}
abstract class Parent_class {}
class User extends Parent_class {
// ...
}
class File extends Parent_class {
// ...
}
并在函数中使用它
function log (Parent_class $requester) {
// ... code
}