为什么不能在PHP中使用泛型

为什么我们不能在PHP中使用泛型

我们将深入探讨泛型和PHP背后的情况。理解为什么泛型在 PHP 中还不被支持作为一等公民,这非常有趣,并且非常重要。

让我们看看吧。

Laravel

PHP中没有泛型。这就是去年的Nikita的结论。这根本不可行。

为了理解Nikita为什么这么说,我们需要看看如何实现泛型。一般来说,有三种可能的方法;支持泛型的编程语言大多使用这三种方法之一。

第一个称为 单态泛型。 让我们回到这个系列的第一篇文章,在这篇文章中我展示了这个集合示例:

class StringCollection extends Collection
{
    public function offsetGet(mixed $key): string 
    { /* … */ }
}

class UserCollection extends Collection
{
    public function offsetGet(mixed $key): User 
    { /* … */ }
}

我解释了我们可以为需要的集合的每种类型,手动创建集合类的实现。 工作量将是巨大的,会有很多代码,但是它会起作用。

单态泛型正是这样做的,但在幕后自动实现。 在运行时,PHP 不会知道泛型 Collection 类,而是知道两个或多个特定实现:

$users = new Collection<User>();
// Collection_User

$slugs = new Collection<string>();
// Collection_string

单态泛型是一种完全有效的方法。例如,Rust 就使用它们。 其一个优点是有一系列的性能提升,因为在运行时没有更多的泛型类型检查,所以在运行代码之前,这些检查都是分开的。

但是这立刻让我们想到了 PHP 中单态泛型的问题。 PHP 没有像 Rust 那样将一个泛型类分成几个具体实现的显式编译步骤; 最重要的是: 单态泛型确实需要相当多的内存,因为你在制作同一个类的多个副本,但有一些差异。 对于已编译的 Rust 二进制文件来说,这可能不是一个大问题,但对于从中心点(服务器)运行的 PHP 代码来说,这是一个严重的问题;可能每秒处理数百或数千个请求。

下一个选项是具体化泛型。这是一个实现,其中泛型类保持原样,类型信息在运行时动态评估类型信息。C# 和 Kotlin 实现了泛型,它是最接近PHP当前类型系统的,因为PHP在运行时执行所有类型检查。这里的问题是需要大量的核心代码重构才能使具体化泛型发挥作用,你可以想象,随着我们在运行时进行越来越多的类型检查,一些性能开销会逐渐增加。

这将我们带到最后一个选项:在运行时完全忽略泛型。就像它们不在那里一样;毕竟,例如集合类的泛型实现无论如何都可以处理所有类型的输入。

因此,如果我们在运行时忽略所有泛型类型检查,则不会有任何问题。

好吧,没有那么快。 在运行时忽略泛型类型——顺便说一下,它被称为类型擦除,Java 和 Python 会这样做——这给 PHP 带来了一些问题。

举一个例子:PHP 不仅使用类型进行验证,它还使用类型信息将值从一种类型动态转换为另一种类型——这就是我在本系列的第一篇文章中提到的类型杂耍:

function add(int $a, int $b): int 
{
    return $a + $b;
}

add('1', '2') // 3;

如果 PHP 忽略了这个「字符串」集合的泛型类型,并且我们不小心向它添加了一个整数,那么如果泛型类型被删除,它将无法警告我们:

$slugs = new Collection<string>();

$slugs[] = 1; // 1 不会被转换为 '1'

类型擦除的第二个也是更重要的问题 —— 也许你现在已经在屏幕上大喊大叫了 ——是类型消失了。如果泛型类型在运行时被删除,我们为什么要添加它们?

这在 Java 和 Pyton 中是有意义的,因为在使用静态分析器运行代码之前会检查所有类型定义。 例如,Java 在编译代码时会运行一个内置的静态分析器; PHP 根本不会做的事情:没有编译步骤,当然也没有内置的静态类型检查器。

另一方面……类型检查的所有优点,我们在之前的文章中讨论过的那些;它们不是来自 PHP 的内置运行时类型检查器。当 PHP 的类型检查器告诉我们有问题时,我们已经在运行代码了。一个类型错误本质上是让我们的程序崩溃。

相反,类型检查的大部分附加值来自不需要我们运行代码的静态分析器。只要程序员提供足够的类型信息,他们就能很好地确保不会出现运行时类型错误。这并不意味着你的代码中不能有任何错误,但可以编写完全静态检查并且在运行时不会产生任何类型错误的PHP代码。最重要的是:我们在编写代码时获得了所有静态洞察;这是任何类型系统中最有价值的部分,与运行时类型检查无关。

那么我们真的需要运行时类型检查吗?因为这是目前无法在PHP中添加泛型的主要原因:对于PHP来说,在运行时验证泛型类型太复杂或太耗费资源。

下一次见,这是本系列的最后一篇文章。

本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://stitcher.io/blog/generics-in-php...

译文地址:https://learnku.com/php/t/66486

本帖已被设为精华帖!
本文为协同翻译文章,如您发现瑕疵请点击「改进」按钮提交优化建议
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!