NVIDIA 初创加速计划,免费加速您的创业启动 了解详情
写点什么

PHP 8:类型系统改进

  • 2023-07-25
    北京
  • 本文字数:8834 字

    阅读完需:约 29 分钟

PHP 8:类型系统改进

本文属于专题文章《深入浅出 PHP 8》


根据w3tech的数据,PHP 仍然是 Web 上使用最广泛的脚本语言之一,77.3%的网站使用 PHP 进行服务器端编程。PHP 8 带来了许多新特性和其他改进,我们将在本系列文章中进行探讨。PHP 8.0 添加了对多个函数和方法相关特性的支持,其中一些是对现有特性的改进,而另一些则是全新的特性。PHP 8.1 中增强的可调用语法可用于通过可调用对象创建匿名函数。命名函数参数可以与位置参数一起使用,另外还有一个好处,即命名参数没有顺序,可以通过它们的名称来传达含义。纤程(Fiber)是可中断的函数,增加了对多任务的支持。


在本文中,我们将讨论 PHP 8、8.1 和 8.2 对 PHP 类型系统的扩展,其中包括联合类型、交集类型和mixed 类型,以及返回类型staticnever


此外,PHP 8 还支持独立类型truenullfalse

一些定义


在 PHP 中,类型声明与类属性、函数参数和函数返回类型一起使用。我们经常使用各种定义从类型系统方面描述一种语言:强/弱,动态/静态。


PHP 是一种动态类型语言。所谓动态类型是指类型检查是在运行时进行的,与之相对的是在静态编译时进行类型检查。PHP 默认是弱类型的,这意味着它在运行时支持的隐式类型转换规则比较少。不过,在 PHP 中可以启用强类型。


PHP 会在不同的上下文中使用类型:


  • 独立类型:可以在类型声明中使用的类型,如intstringarray

  • 字面量类型:除了值的类型之外,还对值本身进行检查的类型。PHP 支持两种字面量类型:truefalse

  • 单元类型:保存单个值的类型,如null。除了简单类型之外,PHP 8 还引入了复合类型,如联合类型交集类型联合类型是多个简单类型的并集。其值只需匹配联合类型中的一种类型。联合类型可用于指定类属性、函数形参的类型或函数返回类型。新增类型mixed 是联合类型的一种特殊类型。


PHP 8.1 还增加了交集类型,用于说明那种是多个类类型的交集的类类型。它还增加了两个新的返回类型。如果函数无返回值,则使用返回类型never 。例如,当函数抛出异常或调用exit()时,可能出现这种情况。返回类型static意味着返回值必须是调用该方法的类的实例。

联合类型


如果你熟悉文氏图,那么你可能还记得集合的并集和交集。为了支持简单类型的并集,PHP 8 引入了联合类型。用于声明联合类型的语法如下:


Type1|Type2|....|TypeN
复制代码


我们首先看个例子。在下面的脚本中,$var1属于联合类型int|string|array 。它被初始化为一个整数值,然后它被设置为联合类型声明中包含的其他类型的值。


<?php
class A{
public int|string|array $var1=1; }
$a= new A();echo $a->var1;$a->var1="hello";echo $a->var1;$a->var1=array( "1" => "a", "2" => "b",);var_dump($a->var1);
复制代码


上述脚本输出如下:


1helloarray(2) { [1]=> string(1) "a" [2]=> string(1) "b" }
复制代码


由于 PHP 是弱类型语言,如果将$var1的值设置为float1.0,就会执行隐式转换。下面的脚本将输出1


<?php
//declare(strict_types = 1);class A{
public int|string|array $var1=1; }
$a= new A(); $a->var1=1.0;echo $a->var1;
复制代码


然而,如果在声明时启用了强类型declare(strict_types = 1) ,那么就不能将$var1 设置为1.0 ,否则会报下面这个错:


Uncaught TypeError:无法将浮点数赋给array|string|int类型的属性A::$var1
复制代码


有时候,在弱类型的情况下,可以将值转换为密切相关的类型,但这种转换并非总能执行。例如,我们不能像下面的脚本那样,给联合类型(int|array )的变量赋字符串值:


<?php
class A{
public int|array $var1=2; }
$a= new A(); $a->var1="hello";echo $a->var1;
复制代码


上述脚本会报如下错误:


Uncaught TypeError: 无法将字符串赋给array|int类型的属性A::$var1
复制代码


我们看一个稍微复杂一点的例子。下面的脚本在类属性声明、函数参数和函数返回类型中使用了联合类型。


<?php
class A{
public string|int|bool $var1=true;
function fn1(string|int|array $a, object|string $b):
string|bool|int { return $a;}
}$a=new A();echo $a->var1;echo $a->fn1("hello","php");
复制代码


输出如下:


1hello
复制代码

联合类型中的 null


联合类型可以为空,在这种情况下,null是联合类型声明中的类型之一。在下面的脚本中,类属性、函数参数和函数返回类型都声明为可空的联合类型。


<?php
class A{
public string|null $var1=null;
function fn1(string|int|null $a=null, object|false|null $b=null):
string|bool|null { return null;}
}$a=new A();echo $a->var1;echo $a->fn1();
复制代码

联合类型中的 false


伪类型false可用于联合类型。在下面的示例中,false类型用于类属性声明、函数参数和函数返回类型,它们全都声明为联合类型。


<?php
class A{
public string|int|false $var1=1;
function fn1(string|int|false $a, false|string $b):
string|false|int { return $a;}
}$a=new A();echo $a->var1;echo $a->fn1("hello",false);
复制代码


输出如下:


1hello
复制代码


如果在联合类型中使用了bool,则不能再使用false,那会被认为是重复声明。考虑下面的脚本,其中一个函数在声明参数时使用了包含falsebool的联合类型。


<?phpfunction fn1(string $a, bool|string|false  $b): object {    return $b;} 
复制代码


执行上述脚本会显示如下错误信息:


重复类型false是多余的
复制代码

联合类型中的 class 类型


Class 类型可以用于联合类型。如下所示,在联合类型中使用 class 类型A


<?php
class A{} function fn1(string|int|A $a, array|A $b): A|string { return $a;}$a=new A();var_dump(fn1($a,$a));
复制代码


输出如下:


object(A)#1 (0) { }
复制代码


但是,如果联合类型中使用了object 类型,就不能再使用 class 类型了。下面的脚本在联合类型中同时使用了 class 类型和object


<?php
class A{} function fn1(object|A $a, A $b): A { return $a;}
复制代码


执行上述脚本会显示如下错误信息:


类型A|object中同时包含object和class类型,这是多余的
复制代码


如果联合类型中使用了iterable,则不能再使用arrayTraversable。下面的脚本在联合类型中同时使用了arrayiterable


<?php
function fn1(object $a, iterable|array $b): iterable { }
复制代码


执行上述脚本会显示如下错误信息:


类型iterable|array中同时包含iterable和array类型,这是多余的
复制代码

联合类型与类继承

如果一个类继承了另一个类,那么联合类型可以单独声明两个类,或者只声明超类。例如,在下面的脚本中,类C继承了类B,类B又继承了类A。然后,在声明联合类型的函数参数时把类ABC都包含了进去。


<?php
class A {function fn1(){ return "Class A object";}}
class B extends A {function fn1(){ return "Class B object";}}
class C extends B {function fn1(){ return "Class C object"; }}
$c = new C();
function fn1(A|B|C $a, A|B|C $b): string { return $a->fn1();}
echo fn1($c,$c);
复制代码


输出如下:


Class C object
复制代码


或者,在声明fn1 的参数时可以只使用A 类型,输出不变:


  function fn1(A $a, A $b): string {         return $a->fn1();}
复制代码

联合类型中的 void


不能在联合类型的返回类型中使用void 。执行如下脚本:


<?php
function fn1(int|string $a, int|string $b): void|string { return $a;}
复制代码


将显示如下错误信息:


Void只能作为独立类型使用
复制代码

联合类型的隐式类型转换


前面我们提到过,如果不启用强类型,那么当一个值与联合类型中的任何类型都无法匹配时,它将转换为与之密切相关的类型。但是,哪些是密切相关的类型呢?隐式转换会按以下优先顺序进行:


  1. 整型

  2. 浮点型

  3. 字符串型

  4. 布尔型例如,在下面的脚本中,字符串值"1"将被转换成一个浮点数:


<?php
class A{
public float|bool $var1=true; }
$a= new A(); $a->var1="1";var_dump($a->var1);?>
复制代码


输出如下:


float(1)
复制代码


但是,如果联合类型中包含int,则输出为int(1)。在下面的脚本中,联合类型(int|float)变量被赋值为字符串"1.0"


<?php
class A{
public int|float $var1=1; }
$a= new A(); $a->var1="1.0";var_dump($a->var1);?>
复制代码


输出如下:


float(1)
复制代码


在下面的脚本中,字符串值"true"会被解释成string 值,因为联合类型中包含string


<?php
class A{
public float|bool|string $var1=1; }
$a= new A(); $a->var1="true";var_dump($a->var1);?>
复制代码


输出如下:


string(4) "true"
复制代码


但是,在下面的脚本中,字符串值"true"会被转换成bool 值,因为联合类型中不包含string


<?php
class A{
public float|bool $var1=1; }
$a= new A(); $a->var1="true";var_dump($a->var1);?>
复制代码


输出如下:


bool(true)
复制代码


下面这个例子的输出难以预测。这个脚本将一个字符串值赋给联合类型为int|bool|float的变量。


<?php
class A{
public int|bool|float $var1=1; }
$a= new A(); $a->var1="hello";var_dump($a->var1);?>
复制代码


输出如下:


bool(true)
复制代码


string被转换为bool值,因为它无法转换为intfloat

新增的 mixed 类型


PHP 8 引入了一个名为mixed的新类型,它相当于联合类型object |resource|array|string|int|float|bool|null。例如,在下面的脚本中,mixed被用作类属性类型、函数参数类型和函数返回类型。启用强类型是为了证明mixed不受强类型所影响。


<?phpdeclare(strict_types = 1);
class A{
public mixed $var1=1; function fn1(mixed $a):mixed{ return $a;}}
$a= new A(); var_dump($a->fn1(true));var_dump($a->var1);$a->var1="hello";var_dump($a->var1);
复制代码


显然,mixed非常灵活,可以输出不同类型:


bool(true) int(1) string(5) "hello"
复制代码


在联合类型中将其他标量类型与混合类型一起使用是多余的,因为mixed类型是所有其他标量类型的联合类型。请看下面的脚本,在一个联合类型中同时使用intmixed类型。


<?php class A{
function fn1(int|mixed $a):mixed{ return $a;}}
复制代码


执行脚本会显示如下错误信息:


类型mixed只能作为独立类型使用
复制代码


类似地,mixed 也不能和任何类类型一起使用。下面的脚本会显示同样的错误信息:


<?php class A{}class B{
function fn1(A|mixed $a):mixed{ return $a;}}
复制代码


子类的方法可以缩小返回类型mixed。例如,子类中的函数fn1将返回类型mixed缩小为array


<?php class A{public function fn1(mixed $a):mixed{ return $a;}}class B extends A{
public function fn1(mixed $a):array{ return $a;
}
复制代码

新增的独立类型 null、false 和 true


在 PHP 8.2 之前,null类型是 PHP 的单元类型,即保存单个值的类型。类似地,false类型是bool类型的字面量类型。不过,nullfalse类型只能在联合类型中使用,而不能作为独立类型使用。要证明这一点,可在 PHP 8.1 及更早的版本中运行如下脚本:


<?php
class A{
public null $var1=null;
}$a=new A();echo $a->var1;
复制代码


在 PHP 8.1 中,该脚本会输出以下错误信息:


Null不能作为独立类型使用
复制代码


类似地,为了证明在 PHP 8.1 或更早的版本中,false类型不能作为独立类型使用,可运行以下脚本:


<?php
class A{
public false $var1=false;
}
复制代码


在 PHP 8.1 中,该脚本会生成如下错误信息:


False不能作为独立类型使用
复制代码


PHP 8.2 支持将nullfalse作为独立类型来使用。下面的脚本使用null作为方法参数类型和方法返回类型。


<?php
class NullExample { public null $nil = null; public function fn1(null $v): null { return null; }}
复制代码


null不能使用?null 显式标记为可空。要证明这一点,可运行以下脚本:


<?php class NullExample {    public null $nil = null;     public function fn1(?null $v): null { return null;  }}
复制代码


该脚本会生成以下错误信息:


null不能标记为可空
复制代码


以下脚本将false 作为独立类型来使用:


<?php class FalseExample {    public false $false = false;     public function fn1(false $f): false { return false;}}
复制代码


nullfalse 可以用于联合类型,如下所示:


<?php class NullUnionExample {    public null $nil = null;     public function fn1(null $v): null|false { return null;  }}
复制代码


此外,PHP 8.2 还新增了一个类型true,它可以用作独立类型使用。下面的脚本使用true作为类属性类型、方法参数类型和方法返回类型。


<?php
class TrueExample { public true $true = true; public function f1(true $v): true { return true;}}
复制代码


true 类型不能和false一起用于联合类型,如下所示:


<?php class TrueExample {     public function f1(true $v): true|false { return true;}}
复制代码


该脚本会生成以下错误信息:


类型同时包含true和false,应该用bool来替代它们
复制代码


类似地,true 也不能和bool 一起用于联合类型,如下所示:


class TrueExample {        public function f1(true $v): true|bool { return true;}}
复制代码


该脚本会生成以下错误信息:


重复类型true是多余的
复制代码

交集类型


PHP 8.1 将交集类型作为复合类型引入。交集类型可以与类和接口类型一起使用。交集类型用于表示多个类和接口类型的类型,而不是单个类或接口类型的类型。交集类型的语法如下:


Type1&Type2...TypeN
复制代码


何时使用交集类型,何时使用联合类型?如果一个类型表示多个类型中的一个,则使用联合类型。如果一个类型要同时表示多个类型,则使用交集类型。下面这个例子很好地说明了这种差异。ABC是 3 个相互之间没有关系的类。如果一个类型要表示这些类型中的任何一个,则要使用联合类型,如下所示:


<?php
class A{ function fn1(){ return "Class A object"; }}
class B { function fn1(){ return "Class B object"; }}
class C { function fn1(){ return "Class C object"; }}
$c = new C();
function fn1(A|B|C $a, A|B|C $b): string { return $a->fn1();}
echo fn1($c,$c); ?>
复制代码


输出如下:


Class C object
复制代码


这里如果使用交集类型,就会产生错误。修改这个函数,使用交集类型:


function fn1(A&B&C $a, A&B&C $b): string {         return $a->fn1();}
复制代码


执行上述脚本会产生以下错误信息:


Uncaught TypeError: fn1(): 参数 #1 ($a)的类型必须是A&B&C,但提供了C
复制代码


如果C 继承了BB 继承了A ,就可以使用交集类型了,如下所示:


<?php
class A{ function fn1(){ return "Class A object";}}
class B extends A{ function fn1(){ return "Class B object";}}
class C extends B{ function fn1(){ return "Class C object"; }}
$c = new C();
function fn1(A&B&C $a, A&B&C $b): string { return $a->fn1();}
echo fn1($c,$c); ?>
复制代码


输出如下:


Class C object
复制代码

标量类型与交集类型


交集类型只能与类和接口类型一起使用,但不能与标量类型一起使用。为了证明这一点,修改前述脚本中的fn1函数,如下所示:


function fn1(A&B&C&string $a, A&B&C $b): string {     }
复制代码


执行上述脚本会产生以下错误信息:


string类型不能作为交集类型的一部分
复制代码

交集类型与联合类型


交集类型不能与联合类型组合使用。具体来说,在同一类型声明中,交集类型表示法不能与联合类型表示法组合使用。为了证明这一点,修改fn1函数如下:


function fn1(A&B|C $a, A&B|C $b): string {    }
复制代码


上述脚本会导致如下解析错误信息:


Parse error: 语法错误,需要变量,但意外遇到符号“|”
复制代码


在同一个函数声明中,交集类型可以与联合类型一起使用,如下所示:


function fn1(A&B&C $a, A|B|C $b): string {       }
复制代码

返回类型 static 与 never


PHP 8.0 引入了一个新的返回类型static ,PHP 8.1 引入了一个新的返回类型 never

返回类型 static


如果返回类型指定为static ,则返回值的类型必须是定义方法的类的类型。例如,类A中定义的fn1方法返回类型为static,因此,该方法必须返回类型为A的值,即声明该函数的类。


<?php
class A { public int $var1=1; public function fn1(): static { return new A(); }}
$a=new A();echo $a->fn1()->var1;
复制代码


输出如下:


1
复制代码


返回类型声明为static 的函数必须属于某个类。为了证明这一点,声明一个返回类型为static 的全局函数:


<?php
function fn1(): static { }
复制代码


上述脚本会导致如下错误:


当没有处于活动状态的类作用域时,不能使用“static”
复制代码


返回的类对象必须是外围类。下面的脚本会生成一个错误,因为返回值是类类型B,而返回类型static 要求返回类型为类型A


<?php
class B{}class A { public int $var1=1; public function fn1(): static { return new B(); }}
复制代码


上述脚本会产生以下错误:


Uncaught TypeError: A::fn1(): 返回类型必须为类型A,但返回了B 
复制代码


如果类B 继承了类A ,那么上述脚本就不会有什么问题,将输出1


class B extends A{}
复制代码


返回类型static 可用于联合类型。如果在联合类型中使用static ,则返回值不一定是类类型。例如,以下脚本在联合类型中使用了static


<?php
class A { public int $var1=1; public function fn1(): static|int { return 1; }}
$a=new A(); echo $a->fn1();
复制代码


输出如下:


 1
复制代码


类型static 不能用于交集类型。为了证明这一点,请看下面的脚本:


<?php
class B extends A{}class A { public function fn1(): static&B { return new B(); }}
复制代码


该脚本会导致以下错误:


类型static不能作为交集类型的一部分
复制代码

返回类型 never


如果返回类型为never,则函数必须不返回值,或者根本不返回,即函数不终止。返回类型never 是其他所有返回类型的子类型。也就是说,在继承一个类时,never 可以在重写方法中替换任何其他返回类型。返回never 的函数必须执行以下操作之一:


  1. 抛出一个异常

  2. 调用 exit()

  3. 启动一个无限循环如果返回never 的函数永远不会被调用,那么这个函数可以为空,如下所示:


<?php
class A{function fn1(): never { } }
复制代码


A中的函数fn1()不能被调用,因为该函数隐式返回NULL 。为了证明这一点,我们将上述脚本修改为:


<?php
class A {function fn1(): never { } }
$a=new A();$a->fn1();
复制代码


执行该脚本将生成以下错误信息:


Uncaught TypeError: A::fn1(): 返回never的函数不能隐式返回
复制代码


下面的脚本将生成同样的错误消息,因为if条件永远无法满足,而函数隐式返回NULL


<?php
function fn1(): never{ if (false) { exit(); }}fn1();
复制代码


static 返回类型不同,never可以用作不属于类作用域的函数的返回类型,例如:


<?php
class A{ } function fn1(): never { }
复制代码


返回类型为never的函数一定不能返回值。为了证明这一点,下面的脚本声明了一个函数,该函数试图返回值,尽管它的返回类型为never


<?php
function fn1(): never { return 1; }
复制代码


执行上述脚本会生成以下错误信息:


返回never的函数必须没有返回值
复制代码


如果返回类型为never,则函数即使是隐式返回也不行。例如,下述脚本中的fn1函数不返回值,而是在其作用域结束时隐式返回。


<?php
function fn1(): never { }
fn1();
复制代码


上述脚本会导致以下错误:


Uncaught TypeError: fn1(): 返回never的函数不能隐式返回
复制代码


声明返回类型为never且不会终止的函数有什么用?返回类型never 可以在开发、测试和调试期间使用。返回never类型的函数可以通过调用exit()退出。这样的函数甚至可以被调用,如下面的脚本所示:


<?php
function fn1(): never { exit(); }
fn1();
复制代码


返回never类型的函数可以抛出异常,如下所示:


<?php
function fn1(): never { throw new Exception('Exception thrown'); }
复制代码


包含无限循环的函数可以将返回类型声明为never ,如下所示:


<?php
function fn1(): never { while (1){}}
复制代码


返回类型never 可以覆盖派生类中的任何类型,如下所示:


<?phpclass A{     function fn1(): int {        }}class B extends A{function fn1(): never {      }}
复制代码


返回类型never 不能用于联合类型。为了证明这一点,下面的脚本在联合类型中使用了never


<?php class A{  function fn1(): never|int {      }}
复制代码


该脚本将导致以下错误:


never只能作为独立类型使用
复制代码


never 类型不能用于交集类型。为了证明这一点,请运行以下将never和类类型B 一起使用的脚本。


<?php class B{}class A{  function fn1(): never&B {      }}
复制代码


该脚本将导致以下错误:


类型never不能作为交集类型的一部分
复制代码

标量类型不支持别名


从 PHP 8 开始,如果使用标量类型别名,就会生成警告信息。例如,如果使用boolean代替bool,则生成一条消息,说明boolean 将被解释成类名。为了证明这一点,考虑下面的脚本,函数声明将integer作为参数类型。


<?php    function fn1(integer $param) {}    fn1(1);?>
复制代码


如下所示,该脚本的输出中将包含一条警告信息:


警告:“integer”将被解释为类名。你是指“int”吗?输入“\integer”来消除该警告
Fatal error: Uncaught TypeError: fn1():参数#1 ($param)必须是integer类型,但提供的是int
复制代码

不再支持从 void 函数通过引用返回


从 PHP 8.1 开始,不再支持从void函数通过引用返回,因为只有变量引用可以通过引用返回,而void返回类型不返回值。为了证明这一点,可运行下面的脚本:


<?phpfunction &fn1(): void {}?>
复制代码


该脚本将输出一条弃用提示信息:


弃用:不再支持从void函数通过引用返回
复制代码

小结


在本文中,我们讨论了 PHP 8 中引入的与类型相关的新特性,包括联合类型、交集类型和mixed 类型,以及返回类型staticnever 。在下一篇文章中,我们将介绍与 PHP 数组、变量、运算符和异常处理相关的新特性。


原文链接:

https://www.infoq.com/articles/php-8-type-system-improvements/


相关阅读:

PHP 8:注解、match 表达式及其他改进

PHP 8:类和枚举

2023-07-25 08:002858

评论 1 条评论

发布
用户头像
当年的屠龙的勇士,最终成为了龙
2023-07-26 14:52 · 北京
回复
没有更多了
发现更多内容

OpenHarmony开发之MQTT讲解

OpenHarmony开发者

OpenHarmony

三面阿里java后台开发岗总结:进阿里必看这份究极面试文档

钟奕礼

Java 编程 java程序员 java面试 java架构

让迁移不再开盲盒,让云也能省钱丨Hackathon 项目背后的故事第一期回顾

PingCAP

hackathon

智慧公路筑基者!天翼云打造全栈能力新底座

天翼云开发者社区

探究并发和并行、同步和异步、进程和线程、阻塞和非阻塞、响应和吞吐等

C++后台开发

多线程 后端开发 并行 linux开发 C++开发

如何杜绝 spark history server ui 的未授权访问?

明哥的IT随笔

hadoop spark

LED透明屏焊接和插接安装以及三招提升稳定性

Dylan

LED LED显示屏 led显示屏厂家

旺链科技出席Hyperledger区块链技术峰会,分享数字乡村新业态

旺链科技

区块链 hyperledger 产业区块链 企业号十月PK榜

4.0体验站|我对OceanBase 4.0社区版的体验与看法

OceanBase 数据库

Kubectl 命令总结

蜗牛也是牛

天翼云打造自研云操作系统TeleCloudOS4.0 推动算力蓬勃发展

天翼云开发者社区

欢迎来嫖!阿里P8高级技术专家携这份818页Java核心技术重磅来袭

钟奕礼

Java 编程 计算机 java程序员 java架构

阿里云研发工程师刘睿:阿里云消息生态及最佳实践

云布道师

阿里云 云原生

Linux内存泄露案例分析和内存管理分享

京东科技开发者

负载均衡 集群 内存泄漏 Linux Cron 运维、

双11狂欢背后,火山引擎数智平台为品牌做了这件事

字节跳动数据平台

大数据 营销数字化 火山引擎

从零到一构建完整知识体系!阿里巴巴Java并发编程技术内幕全网首次公开

Java全栈架构师

源码 程序员 程序人生 Java并发 java面试

共享开源技术,共建开放生态丨平凯星辰余梦杰出席 2022 世界互联网大会开源论坛圆桌对话

PingCAP

开源

天翼云实时云渲染,助力打造世界VR产业大会云上之城

天翼云开发者社区

TiKV 源码阅读三部曲(三)写流程

PingCAP

源码阅读

视频服务HDR Vivid 还原色彩,让所见成“真”

HMS Core

视频 HMS Core

【#HDC2022】HarmonyOS体验官活动正式开启,赶快投稿赢限量奖品吧!

HarmonyOS开发者

HarmonyOS

数字先锋| 教育资源乘云而来!46万城乡学子共享名师课堂

天翼云开发者社区

java文件流

hello java

文件 程序 Java core 11月月更

Jmix 1.4 功能概览

世开 Coding

企业级低代码 Jmix 企业级应用程序开发

【从零开始学爬虫】采集同花顺基金评论数据

前嗅大数据

数据采集 爬虫软件 爬虫教程 数据采集教程 爬虫案例

钢网有多个种类,各自的用法都了解吗?

华秋PCB

PCB PCB设计 PCB生产

这次,听人大教授讲讲分布式数据库的多级一致性|TDSQL关键技术突破

腾讯云数据库

腾讯云 tdsql 腾讯云数据库 多级一致性 中国人民大学

太强了!终于有人整理出了仿京东电商项目,再次开源了

钟奕礼

Java 编程 架构 项目 java程序员

手慢无!清华大牛熬夜整理Spring微服务架构设计第2版文档,限时删

钟奕礼

Java 编程 架构 计算机 java程序员

面向对象基础

断墨寻径

面向对象 java;

快速满足个性化业务需求的低代码平台

力软低代码开发平台

PHP 8:类型系统改进_编程语言_Deepak Vohra_InfoQ精选文章