作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
早在2013年,达特的官方1.0的发布受到了一些压力——就像大多数谷歌的发行一样——但并不是每个人都如此热切 作为b谷歌的内部团队 用飞镖语言创建关键业务应用程序. 五年后,它经过深思熟虑重建了第二段, b谷歌似乎已经证明了它对这门语言的承诺. 事实上,今天它继续受到开发人员的青睐——尤其是Java和c#老手.
飞镖编程语言非常重要,原因如下:
我们这些在大型企业系统中使用c#或Java的人已经知道为什么要使用类型安全, 编译时错误, 毛是很重要的. 我们中的许多人都对采用“脚本化”语言犹豫不决,因为害怕失去所有的结构, 速度, 精度, 以及我们习惯的可调试性.
但是随着飞镖的开发,我们不需要放弃这些. 我们可以编写一个移动应用程序, web客户端, 和后端使用相同的语言,并获得我们仍然喜欢的Java和c#的所有东西!
为此目的, 让我们浏览一些关键的飞镖语言示例,这些示例对于c#或Java开发人员来说可能是新的, 最后我们会在飞镖语言的PDF中总结一下.
注意:本文仅涵盖第2部分.x. 版本1.尤其是X还没有“完全熟”, 类型系统是建议的(像TypeScript)而不是必需的(像c#或Java).
第一个, 我们将讨论最重要的区别之一:代码文件是如何组织和引用的.
在c#中,类的Collections被编译成程序集. 每个类都有一个名称空间, 名称空间通常反映了文件系统中源代码的组织-但最终, 程序集不保留有关源代码文件位置的任何信息.
在Java中, 源文件是包的一部分,命名空间通常与文件系统位置一致, 但最后, 包就是类的Collections.
因此,这两种语言都有一种方法来保持源代码在某种程度上独立于文件系统.
相比之下, 用飞镖语言, 每个源文件必须导入它所引用的所有内容, 包括您的其他源文件和第三方包. 没有相同的名称空间,您通常通过文件系统位置引用文件. 变量和函数可以是顶级的,而不仅仅是类. 在这些方面,飞镖更像脚本.
因此,您需要将您的想法从“类的Collections”转变为更像是“包含的代码文件序列”.”
飞镖既支持包组织,也支持没有包的特别组织. 让我们从一个没有包的例子开始,来说明包含文件的顺序:
/ / file1.飞镖
int alice = 1; // top level variable
int barry() => 2; // top level function
var student = Charlie(); // top level variable; Charlie is declared below but that's OK
类查理{ ... } //顶级类
// alice = 2; // top level statement 不 allowed
/ / file2.飞镖
进口的file1.飞镖'; // causes all of file1 to be in scope
main () {
print(alice); // 1
}
源文件中引用的所有内容都必须在该文件中声明或导入, 因为没有“项目”级别,也没有其他方法可以在范围中包含其他源元素.
飞镖中名称空间的唯一用途是为导入指定一个名称, 这将影响您如何引用从该文件导入的代码.
/ / file2.飞镖
进口的file1.达特是仙境;
main () {
print(仙境.alice); // 1
}
上面的例子在没有包的情况下组织代码. 为了使用包,代码以更具体的方式组织起来. 下面是一个名为 苹果
:
苹果/
酒吧spec.yaml
-定义包名、依赖项和其他一些东西lib /
苹果.飞镖
—imports 和 exports; this is the file imported by any consumers of the pack年龄src /
种子.飞镖
-所有其他代码在这里bin /
run苹果.飞镖
-包含main函数, 哪个是入口点(如果这是一个可运行包或包含可运行工具)然后你可以导入整个包而不是单个文件:
进口的包:苹果;
重要的应用程序应该始终组织为包. This alleviates a lot of having to repeat file system paths in each 裁判erring file; plus, 他们跑得更快. 它还可以很容易地共享您的包 酒吧.dev,其他开发人员可以很容易地将其获取以供自己使用. 应用程序使用的包将导致源代码被复制到文件系统中, 因此,您可以按照自己的意愿调试这些包的任何深度.
在飞镖的类型系统中有一些主要的区别需要注意, 关于取消, 数值类型, Collections, 以及动态类型.
来自c#或Java,我们习惯了 原始的 or 价值 不同于 参考 or object 类型. 值类型有, 在实践中, 在堆栈或寄存器中分配, 该值的副本作为函数参数发送. 而是在堆上分配引用类型, 只有指向对象的指针才会作为函数参数发送. 因为值类型总是占用内存, 值类型变量不能为空, 所有值类型成员都必须具有初始化的值.
飞镖 eliminates that distinction because everything is an object; all 类型 ultimately derive from the type Object
. 所以,这是合法的:
Int I = 零;
实际上,所有原语都隐式初始化为 零
. 这意味着您不能像在c#或Java中那样假定整数的默认值为零, 您可能需要添加空检查.
有趣的是,即使是 零
是型,又是字 零
指…的实例 零
:
print(零.runtimeType); // prints 零
不像我们熟悉的从8位到64位的整数类型,有符号和无符号, 飞镖的主要整数类型是 int
, 64位值. (也有 长整型数字
对于非常大的数字.)
因为没有字节数组作为语言语法的一部分, 二进制文件内容可以作为整数列表来处理, i.e. 列表
.
如果你认为这一定是非常低效的,设计师已经想到了. 在实践中, 根据运行时使用的实际整数值,有不同的内部表示. 对象分配堆内存 int
对象,如果它可以优化掉,并在未装箱模式下使用CPU寄存器. 还有图书馆 byte_data
提供了 UInt8列表
还有一些其他的优化表示.
Collections和泛型与我们习惯的非常相似. 需要注意的主要问题是,没有固定大小的数组:只需使用 列表
在使用数组的地方使用数据类型.
此外,还提供了初始化三种Collections类型的语法支持:
最后 a = [1, 2, 3]; // inferred type is 列表, an array-like ordered collection
最后 b = {1, 2, 3}; // inferred type is 集, an unordered collection
最后的c = {'a': 1, 'b': 2}; // inferred type is Map, 名称-值对的无序Collections
所以,使用飞镖吧 列表
你可以使用Java数组, Array列表
, or 向量
; or a C# array or 列表
. 使用 集
你会在哪些地方使用Java/ c# Hash集
. 使用 Map
你会在哪里使用Java HashMap
或c# 字典
.
在像JavaScript这样的动态语言中, Ruby, 和Python, 即使成员不存在,也可以引用它们. 下面是一个JavaScript示例:
var 人 = {}; // create an 电磁脉冲ty object
人.name = 'alice'; // add a member to the object
如果(人.年龄 < 21) { // 裁判er to a property that is 不 in the object
// ...
}
如果你运行这个, 人.年龄
将 未定义的
,但它还是运行.
同样,你也可以在JavaScript中改变变量的类型:
var a = 1; // a is a number
a = 'one'; // a is now a string
相比之下, 在Java中, 您不能像上面那样编写代码,因为编译器需要知道类型, 它检查所有的操作都是合法的——即使你使用var关键字:
var b = 1; // a is an int
// b = "one"; // 不 allowed 在Java中
Java只允许使用静态类型编写代码. (您可以使用自省来执行一些动态行为,但它不是语法的直接组成部分.JavaScript和其他一些纯动态语言只允许你编写动态类型的代码.
飞镖语言允许以下两种情况:
/ /飞镖
动态 a = 1; // a is an int - 动态 typing
a = 'one'; // a is now a string
a.foo (); // we can call a function on a 动态 object, to be resolved at run time
var b = 1; // b is an int - static typing
// b = 'one'; // 不 allowed in 飞镖
飞镖具有伪类型 动态
哪一种导致在运行时处理所有类型逻辑. 试图打电话 a.foo ()
不会打扰静态分析器和代码将运行, 但它将在运行时失败,因为没有这样的方法.
c#最初和Java很像, 后来又增加了动态支持, 所以飞镖和c#在这方面是差不多的.
飞镖中的函数语法比c#或Java中的更简单、更有趣. 语法是以下任意一种:
//函数作为声明
返回类型名称(参数){主体}
return-type name (parameters) => expression;
//函数表达式(可赋值给变量等.)
身体(参数){}
(parameters) => expression
例如:
void print喷火() { print('foo'); };
字符串 embellish(字符串 s) => s.toUpperCase() + '!!';
var print喷火 = () { print('foo'); };
var embellish = (字符串 s) => s.toUpperCase() + '!!';
因为所有东西都是对象,包括像 int
和 字符串
,参数传递可能会令人困惑. 虽然没有 裁判
像c#一样的参数传递, 一切都是通过引用传递的, 并且函数不能更改调用者的引用. 因为对象在传递给函数时不会被克隆, 函数可以改变对象的属性. 然而, 对于int和字符串这样的原语来说,这种区别实际上是没有意义的,因为这些类型是不可变的.
Var id = 1;
Var name = 'alice';
var 客户端 = 客户端 ();
无效foo(int id, 字符串 name, Client Client) {
id = 2; // local var points to different int instance
name = 'bob'; // local var points to different 字符串 instance
客户端.State = 'AK'; // property of caller's object is changed
}
喷火 (id, name, 客户端);
// id == 1, name == 'alice', 客户端.State == 'AK'
如果你在c#或Java的世界, 你可能已经诅咒过这些令人困惑的重载方法:
/ / java
无效foo(字符串arg1) {...}
Void foo(int arg1, string arg2) {...}
无效foo(字符串arg1,客户端arg2) {...}
//呼叫站点:
foo(客户端Id, input3); // confusing! 很容易误读它调用的是哪个重载
对于c#可选参数,还有另一种混淆:
// c#
void 喷火(字符串arg1, int arg2 = 0) {...}
void 喷火(字符串arg1, int arg3 = 0, int arg2 = 0) {...}
//呼叫站点:
喷火("alice", 7); // legal but confusing! 很容易误读它调用的是哪个重载以及哪个形参绑定到实参7
喷火("alice", arg2: 9); // better
c#不需要在调用地点命名可选参数, 因此,用可选参数重构方法可能是危险的. 如果一些调用站点在重构后恰好是合法的,编译器将不会捕获它们.
飞镖有一种更安全、更灵活的方式. 首先,重载方法是 不 支持. 相反,有两种方法可以处理可选参数:
//位置可选参数
无效foo(字符串arg1, [int arg2 = 0, int arg3 = 0]) {...}
//调用site获取位置可选参数
foo('alice'); // legal
foo('alice', 12); // legal
foo('alice', 12, 13); // legal
//指定可选参数
Void bar(字符串arg1, {int arg2 = 0, int arg3 = 0}) {...}
bar('alice'); // legal
bar('alice', arg3: 12); // legal
bar('alice', arg3: 12, arg2: 13); // legal; sequence can vary 和 names are required
不能在同一个函数声明中同时使用这两种样式.
异步
关键字的位置c#有一个令人困惑的位置 异步
关键字:
Task 喷火() {...}
异步 Task 喷火() {...}
这意味着函数签名是异步的, 但实际上只有函数实现是异步的. 以上任一签名都是该接口的有效实现:
接口ICan喷火 {
Task 喷火();
}
在飞镖语言中, 异步
在一个更合乎逻辑的地方,表示实现是异步的:
Future foo () 异步 {...}
与c#和Java一样,飞镖也是有词法作用域的. 这意味着在块中声明的变量在块结束时超出了作用域. 所以飞镖以同样的方式处理闭包.
Java普及了属性get/set模式,但该语言没有任何特殊的语法:
/ / java
私人 字符串 客户端Name;
公共 字符串 getClientName() { return 客户端Name; }
公共 void setClientName(字符串 价值}{ 客户端Name = 价值; }
c#有相应的语法:
// c#
私有字符串客户端Name;
公共字符串ClientName {
get { return 客户端Name; }
set { 客户端Name = 价值; }
}
飞镖支持属性的语法略有不同:
/ /飞镖
字符串_客户端Name;
string get ClientName => _客户端Name;
string set ClientName(string s) { _客户端Name = s; }
飞镖构造函数比c#或Java有更多的灵活性. 一个很好的特性是能够命名同一类中的不同构造函数:
类点{
点(双x,双y) {...} //默认的动作
点.asPolar(双角度,双r) {...} //命名函数
}
你可以只使用类名调用默认构造函数: var c = Client();
在调用构造函数体之前初始化实例成员有两种简写:
类客户端{
字符串_code;
字符串_name;
客户端(字符串._name) //“this”为实例成员分配参数的简写
: _code = _name.toUpper(){//用于初始化的特殊体外位置
/ /身体
}
}
构造函数可以运行超类构造函数并重定向到同一类中的其他构造函数:
喷火.常量ructor1(int x) : this(x); // redirect to the default ctor in same class; no body allowed
喷火.构造函数2(int x): super.平原(x) {...} //调用名为tor的基类,然后运行此主体
喷火.Constructor3 (int x): _b = x + 1: super.平原(x) {...} //初始化_b,然后调用基类ctor,然后运行这个主体
在Java和c#中,调用同一类中的其他构造函数的构造函数可能会在它们都有实现时变得混乱. 在飞镖, 重定向构造函数不能有函数体的限制迫使程序员使构造函数的层更清晰.
还有一个 工厂
关键字,允许函数像构造函数一样使用, 但是它的实现只是一个普通的函数. 你可以使用它来返回一个缓存实例或派生类型的实例:
类Shape {
工厂形状(int insides) {
if (nsides == 4) return Square();
/ /等.
}
}
var s = Shape(4);
在Java和c#中,我们有这样的访问修饰符 私人
, 受保护的
, 公共
. 在飞镖, 这是非常简化的:如果成员名以下划线开头, it’s visible everywhere inside the pack年龄 (including from other classes) 和 hidden from outside callers; otherwise, 从任何地方都能看到它. 没有关键字像 私人
表示能见度.
另一种修饰符控制可变性:关键字 最后
和 常量
都是为了这个目的,但它们的意思不同:
var a = 1; // a is variable, can be reassigned later
最后 b = a + 1; // b is a runtime 常量ant, can only be assigned once
常量 c = 3; // c is a compile-time 常量ant
// 常量 d = a + 2; // 不 allowed because a+2 can不 be resolved at compile time
飞镖语言支持接口、类和一种多重继承. 然而,没有 接口
keyword; instead, all classes are also 接口s, so you can define an 摘要
类,然后实现它:
抽象类HasDesk {
bool isDeskMessy(); // no implementation here
}
类员工实现HasDesk {
bool isdesk凌乱(){ ...} //必须在这里实现
}
多继承是通过使用 扩展
关键字,以及使用 与
关键字:
类员工扩展人与受薪实现HasDesk {...}
在这份声明中, 员工
类派生自 人
和 受薪
,但 人
主超类是和吗 受薪
是mixin(二级超类).
有一些有趣而有用的飞镖操作符是我们不习惯的.
级联 允许你在任何东西上使用链模式:
电磁脉冲 ..name = 'Alice' ..监事= 'Zoltron' ..雇用();
扩展操作符允许将Collections视为初始化式中的元素列表:
var small列表 = [1,2];
var big列表 = [0, ...small列表, 3, 4]; // [0, 1, 2, 3, 4]
飞镖没有线程,这使得它可以被编译成JavaScript. 取而代之的是“隔离”, 哪个更像是独立的过程, 从某种意义上说,它们不能共享记忆. 由于多线程编程非常容易出错,因此这种安全性被视为飞镖的优点之一. To 隔离间通信, you need to stream data between them; the received objects are copied into the receiving isolate’s memory space.
如果您是c#或Java开发人员, 你已经知道的将帮助你快速学习飞镖语言, 因为它的设计是为了让人熟悉. 为此,我们准备了一个 飞镖小抄PDF 供您参考,特别关注与c#和Java等同物的重要区别:
本文中显示的差异与您现有的知识相结合,将帮助您在使用飞镖的头一两天内提高工作效率. 快乐的编码!
飞镖是一种现代的、类型安全的、易于访问的语言,由谷歌开发. 它可以编译到本地桌面和移动平台,并转换为web应用程序的JavaScript.
作为一种多平台通用语言, 飞镖可以用于web和移动前端和后端, 数据库应用程序, 和脚本. b谷歌AdWords用户界面是用飞镖构建的.
飞镖是一种结合了c#许多最佳特性的语言, Java, Python, 和JavaScript, 例如动态和静态类型, 异步支持, 函数. 它经过精心设计,以避免容易出错的复杂性,并且可以快速学习.
Python和JavaScript通常被推荐为首选语言,因为实践脚本可以快速运行,学习曲线较低. 然而,任何语言都可以成为第一语言,因为一种语言的大多数技能都可以转移到其他语言. 从c#开始学习是有益的,因为它很早就教授了类型安全.
飞镖的语法看起来和行为都很像它的近亲c#. 它还支持从源代码解释脚本,就像Python一样.
飞镖可以提前编译(AOT),也可以及时编译(JIT)。. 飞镖应用程序的部署既可以编译到本机平台,也可以直接从源代码运行.
飞镖可选择性地转换为JavaScript,允许飞镖(和Flutter)应用程序在浏览器中运行. 它还支持编译为本机可执行文件.
世界级的文章,每周发一次.
世界级的文章,每周发一次.