基于 JVM 的新一代编程语言 Fantom 来自 IBM带程序示例.docx
《基于 JVM 的新一代编程语言 Fantom 来自 IBM带程序示例.docx》由会员分享,可在线阅读,更多相关《基于 JVM 的新一代编程语言 Fantom 来自 IBM带程序示例.docx(21页珍藏版)》请在冰豆网上搜索。
![基于 JVM 的新一代编程语言 Fantom 来自 IBM带程序示例.docx](https://file1.bdocx.com/fileroot1/2023-1/2/7f32c8bb-2e0d-4e88-a237-cf58fadc8858/7f32c8bb-2e0d-4e88-a237-cf58fadc88581.gif)
基于JVM的新一代编程语言Fantom来自IBM带程序示例
基于JVM的新一代编程语言:
Fantom
陈晨,软件工程师,IBM
王冬蕾,软件工程师,IBM
赵一铂,软件工程师,IBM
简介:
自从JVM提供非Java语言的支持以来,很多风格迥异的编程语言都加入到JVM的备选语言行列中来。
这其中包括一些“老”编程语言,如JavaScript;也包括一些全新的编程语言,比如Groovy、Scala,以及本文要介绍的Fantom。
api, java_入门, jvm_(java_virtual_machine), 应用开发, 标准, 算法, 编码
标记本文!
Fantom诞生于2007年,相比于Groovy和Scala来说已经算是比较晚了。
但是它吸收了其他语言的长处,形成了自己独特的语法风格,在新一代基于JVM的语言中占有了重要的地位。
本文是对于Fantom编程语言的一个简要介绍,包括了基本语法和一些高级特性,例如对闭包、动态编程(DynamicProgramming),以及函数式编程(FunctionalProgramming)的支持。
本文的主要目标是使Java开发人员可以熟悉Fantom这种语言,并能感受到Fantom结构上的精简和功能上的强大。
需要注意的是,Fantom并不仅仅可以在JVM中运行,还可以在.netCLR(通用语言运行时)和浏览器中运行。
对多种平台的支持也使得Fantom的竞争力得到了进一步的提升。
安装并配置Fantom
要体验Fantom各种有趣的特性,首先需要在您的计算机上安装Fantom。
在Linux和Windows平台上安装的步骤并不相同,这里介绍Linux平台上的安装方法,Windows平台上则可以参考 这里的教程。
首先在 这里下载Fantom的发行包(zip格式)。
发行包里同时包含了Linux和Windows平台下所需的文件。
下载完成之后,将其解压到您选定的目录。
浏览目录下的内容,您会发现很多Fantom的文档以及示例程序等有用的内容。
当然,最重要的是bin目录下的几个可执行文件,尤其是fan/fan.exe,它是Linux/Windows平台下Fantom语言的解释器和编译器。
接下来需要配置环境变量。
定义一个环境变量FANTOM_HOME(或者任何您喜欢的名字),指向解压Fantom的目录。
然后将FANTOM_HOME/bin添加到PATH中。
最后,运行一下adm目录中的unixsetup,为所有可执行文件添加执行权限:
清单1.为Fantom的可执行文件添加权限
bashadm/unixsetup
至此,Fantom的运行环境就配置完毕了。
或许您还希望安装一个集成开发环境(IDE)。
目前支持Fantom的IDE还不是很多,推荐使用基于Eclipse的 FantomIDE。
不过由于Fantom语言的简洁性,使用普通的文本编辑器也一样可以很好的完成开发工作。
接下来,像所有编程语言的介绍一样,我们从“HelloWorld”开始。
不同版本的HelloWorld
使用Fantom解释器
Fantom从本质上来说是一种脚本语言,所以您可以写出如下简洁的HelloWorld脚本:
清单2.Fantom的HelloWorld脚本
classHelloWorld
{
staticVoidmain()
{
echo("Helloworld!
")
}
}
保存为helloworld.fan,然后这样运行它:
清单3.运行HelloWorld脚本
root@computer:
~#fanhelloworld.fan
Helloworld!
直接在Shell中运行
和很多脚本语言一样,Fantom也提供了一个Shell环境。
您同样可以在Shell环境中完成HelloWorld:
清单4.Shell环境中的HelloWorld
root@computer:
~#fansh
FantomShellv1.0.56(‘?
’forhelp)
fansh>echo(“Helloworld!
”)
Helloworld!
fansh>quit
Web应用程序中的HelloWorld
作为JVM的备选语言之一,Java做到的Fantom也能做到。
所以我们也可以把HelloWorld显示到Web页面上。
Fantom中提供了对Web服务器的内置支持(WISP库)。
所以我们不需要第三方的Web服务器就可以实现一个简单的Web应用程序。
下面是Fantom发行包里面附带的一个例子,您可以在example/web/hello.fan中找到:
清单5.Web环境中的HelloWorld
usingutil
usingweb
usingwisp
classWebHello:
AbstractMain
{
@Opt{help="httpport"}
Intport:
=8080
overrideIntrun()
{
wisp:
=WispService
{
it.port=this.port
it.root=HelloMod()
}
returnrunServices([wisp])
}
}
constclassHelloMod:
WebMod
{
overrideVoidonGet()
{
res.headers["Content-Type"]="text/plain;charset=utf-8"
res.out.print("helloworld#4")
}
}
下面对这段代码做一些说明:
∙类WebHello继承自AbstractMain,AbstractMain用于快速地创建应用程序,无论是Web应用程序,还是桌面应用程序,都可以通过继承这个类来方便的创建。
WebHello重载了AbstractMain中的run方法,这个方法是应用程序的入口点,这里用来初始化Web服务器;
∙run方法中声明了一个WispService的实例,这个实例中指定Web服务器的端口(8080)和开启的服务(HelloMod);
∙HelloMod类相当于Java中的Servlet,其中有一个重载的onGet方法,相当于Servlet中的doGet。
在这个例子的onGet方法中,除了指定HTTP头以外,只输出了一行“helloworld#4”。
由于Fantom提供了内置的Web服务器支持,运行这个例子十分简单,只需要使用Fantom解释器处理这段代码:
清单6.运行HelloWorld
root@computer:
~#fanexamples/web/hello.fan
[16:
08:
5721-Dec-10][info][web]WispServicestartedonport8080
看到上面所示的WISP服务启动成功的信息之后,您就可以在浏览器中访问 http:
//localhost:
8080来查看运行结果了,十分方便,是不是?
上面介绍了三种不同形式的使用Fantom语言编写的HelloWorld程序。
仔细观察这些代码,您会发现Fantom的语法和Java有很大的不同。
正是这种独特的语法使Fantom语言的代码十分简洁易懂。
兼容并包的语法
Fantom在语法上有很多和Java相同的地方,包括绝大部分的运算符,if…else结构,while和for循环等。
但做为一种新的编程语言,Fantom也吸收了很多其他编程语言的语法特性。
接下来会举例介绍。
语句
Fantom的语句是以行为单位的,语句的结尾并不需要分号,这一点与Java不同。
但为了照顾众多Java和C++程序员的使用习惯,以分号分隔的语句也同样被支持:
清单7.Fantom的语句
if(true){
dosomething()
return
}//OKforFantom
if(true){
dosomething();
return;
}//OKforFantom
if(true){
dosomething();return;
}//OKforFantomtoo
声明与赋值
对于像Java和C++这样的强类型编程语言来说,声明和赋值语句看起来没什么区别,除了声明语句最左侧的类型定义:
清单8.Java的声明和赋值语句
Stringfoo=“Foo”;//Javadeclaration
foo=“Foo”;//Javaassignment
而对于弱类型的语言,例如Perl来说,声明和赋值语句看起来是完全一样的。
这使得有的时候代码十分难于读懂:
清单9.Perl的声明和赋值语句
$foo=“Foo”;//Perlassignmentordeclaration
而Fantom同时支持强类型和弱类型风格的变量声明:
清单10.Fantom的声明语句
Strfoo:
=“Foo”
foo:
=“Foo”
而赋值语句是这样的:
清单11.Fantom的赋值语句
foo=“Foo”
可以看到声明和赋值时使用的操作符是不一样的。
这使得代码更加清晰易读。
空对象安全的成员引用操作符
在使用Java开发应用程序的时候,一个特别需要注意的地方就是避免对空对象使用成员引用操作符(.),如果对一个为null的对象使用成员引用操作符,会抛出NullPointerException异常。
但在Fantom中,如果您使用像下面这样的空对象安全的(Null-Safe)代码,就可以无需考虑这个问题:
清单12.空对象安全的成员引用
price=fruitMap?
.find(“Apple”)?
.getPrice()
这行代码相当于:
清单13.与清单12等价的代码
Intprice:
=null
if(fruitMap!
=null)
{
fruit:
=fruitMap.find(“Apple”)
if(fruit!
=null)
price=fruit.getPrice()
}
可见如果fruitMap或者fruitMap.find()的返回值为null,则price也为null。
在这个过程中并不会抛出任何异常,而且只需要一条语句,十分简洁。
列表和映射
Fantom中借鉴了Perl的数据类型——列表(List)和映射(Map,Perl中称之为散列——Hash)。
而在Java中实现同样的数据结构则需要使用接口java.util.List和java.util.Map的实现类,很显然Fantom中选用了更简洁的语法。
下面举例说明:
清单14.Fantom的列表
Int[10,20,30]
[10,20,30]
上面两条语句的效果是一样的,在列表中的元素类型已经确定的情况下,前面的数据类型定义可以省略。
Fantom还引入了一种新的数据类型表示,即在数据类型后面加?
,这种新的数据类型的默认值为null,而不是根据类型不同而使用不同的默认值,例如Int,预设的默认值是0,而Int?
的默认值是null。
默认的Int列表是不能够储存null类型的元素的,如果想要实现这一点,需要如下的定义:
清单15.能储存null元素的列表
[10,null,30]
Int?
[10,20,30]
第一条语句中的第二个元素是null,因此列表被定义为Int?
类型,而第二条语句则是显式指定列表为Int?
类型,两条语句的效果是一样的。
上面的例子中我们可以知道,Fantom对于元素类型不统一的列表定义,会为列表使用一个通用的数据类型,下面是几个例子:
清单16.不同类型元素的混合列表
[10,”20”,30]//evaluatestoObj[]
[10,20f,30]//evaluatestoNum[]
对于[10,“20”,30]这个例子,由于第二个元素是Str类型,而另外两个元素是Int类型,因此这个列表的类型是Int和Str的共同基类——Obj。
第二个例子同理,列表的类型是Int和Float的共同基类——Num。
映射用来储存一组键/值对,定义映射的语句如下:
清单17.Fantom的映射
[Int:
Str][1:
”one”,2:
”two”,3:
”three”]
Int:
Str[1:
”one”,2:
”two”,3:
”three”]
[1:
”one”,2:
”two”,3:
”three”]
上面三种定义是等价的。
映射在其元素数据类型不一致时的规则与列表相同,不再赘述。
上述的只是一些Fantom的一些新的语法特点,更详细的介绍可以在Fantom的在线文档库中找到。
简洁优雅的类和容器
类的定义
在了解Fantom的基本语法之后,下面来介绍Fantom中类的定义。
对于Java语言来说,JavaBean是一个很重要的概念,一个典型的JavaBean定义里,通常有若干个private的成员变量,以及用来存取这些成员变量的public的getter和setter方法,另外可能还有一些其他的public方法,如下面的例子:
清单18.Java的类定义
publicclassEmployee{
privateStringname;
privateIntegerage;
privateIntegeryearOfService;
publicStringname(){returnthis.name;}
publicvoidname(Stringname){this.name=name;}
publicIntegerage(){returnthis.age;}
publicvoidage(Integerage){this.age=age;}
publicIntegeryearOfService(){returnthis.yearOfService;}
publicvoidyearOfService(IntegeryearOfService)\
{this.yearOfService=yearOfService}
publicFloatcalculateSalary(){……}
}
而在Fantom中,对应的类只需要很少的代码:
清单19.Fantom的类定义
classEmployee{
Strname
Intage
IntyearOfService
FloatcalculateSalary(){……}
}
Fantom会自动生成对应的getter和setter方法。
如果需要自己重载,也相当简单:
清单20.重载setter方法
classEmployee{
Strname
Intage{set{checkAge(val);&age=it}}
IntyearOfService
FloatcalculateSalary(){……}
}
其中的 it是保留字,表示将要赋给成员变量的值,即setter方法的参数;&age表示成员变量age实际的存储单元。
事实上,Fantom还自动生成了一个类的默认构造函数,这个构造函数以保留字 make为名字:
清单21.类的构造函数
classEmployee{
Strname
Intage
IntyearOfService
newmake(Strname,Intage,IntyearOfService)
{
this.name=name
this.age=age
this.yearOfService=yearOfService
}
}
在Java中,类的构造函数是没有返回类型的,而在Fantom中,构造函数前需要加保留字 new以示不同。
Fantom中的容器
在Java语言中,代码的管理是以包(package)为单位的。
包定义了一个名字空间(namespace),使得在代码量较大的时候管理起来比较容易,同时也使得不同名字空间下的代码相互隔离。
包的名称是层次化的,整个代码库是一个树状结构:
图1.Java的代码结构
包的引入解决了名字混乱的问题,但是如果包的层次比较多的话,整个代码结构仍然会变得难以读懂。
而在Fantom中,包的概念被容器(Pod)所代替,代码被组织为两个层次:
容器和类。
于是代码呈现一种扁平的两层结构:
图2.Fantom的代码结构
实际上Fantom中的容器不仅能完成代码管理的工作,还能提供代码部署时的支持,在这点上,它相当于Java中的Jar文件,或者.NET平台上的DLL文件。
我们可以把之前的HelloWorld程序包装进容器中,虽然对于这种规模很小的程序没有什么用处,但是可以通过这个过程了解Fantom下容器的概念,以及打包代码库的方法。
这里以Linux环境下为例。
首先我们需要设置容器的目录结构,选择一个目录作为容器的工作目录,过一会儿我们要在这个目录下放置编译脚本。
再选择一个目录作为代码目录,放置待编译的代码,为了方便,我们把代码目录放在工作目录下,结构如下所示:
清单22.目录结构
hello/
build.fan
source/
hello.fan
这里的build.fan就是编译脚本,实际上它也是以Fantom源代码形式编写的,内容如下:
清单23.编译脚本
classBuild:
build:
:
BuildPod
{
newmake()
{
podName="hello"
summary="helloworldpod"
depends=["sys1.0"]
srcDirs=[`source/`]
}
}
我们可以看到,这个编译脚本实际上创建了一个BuildPod的派生类,并在构造函数中设置了必要的一些参数。
容器build中提供了很多用于编译Fantom代码库的功能,读者们可以参考Fantom的文档库查询详细的介绍。
对于HelloWorld程序来说,我们只需要一个源代码文件hello.fan,它的内容如下:
清单.24HelloWorld代码
classMain
{
staticVoidmain(){echo("Helloworld!
")}
}
由于编译脚本实际上也是Fantom源代码,我们只需要使用Fantom解释器运行这段脚本就能完成编译:
清单25.编译HelloWorld容器
root@computer:
~/hello#fanbuild.fan
compile[hello]
Compile[hello]
FindSourceFiles[1files]
WritePod[file:
/usr/local/fantom/lib/fan/hello.pod]
BUILDSUCCESS[39ms]!
代码已经编译完成了,我们可以在命令行中访问容器里的方法:
清单26.在Shell中访问容器中的方法
root@computer:
~/hello#fanhello
Helloworld!
root@computer:
~/hello#fanhello:
:
Main
Helloworld!
root@computer:
~/hello#fanhello:
:
Main.main
Helloworld!
同样也可以在代码里访问容器里的方法。
创建一个新的文件AccessPod.fan,加入如下代码:
清单27.在代码里访问容器中的方法
usinghello
classAccessPod{
staticVoidmain(){hello:
:
Main.main()}
}
root@computer:
~#fanAccessPod.fan
Helloworld!
在前面的例子中我们可以看到,编译Fantom源代码得到的是.pod文件,这种文件中包含了编译Fantom源代码而生成的字节码(我们称之为Fcode)、常量池以及程序运行必须的资源文件等。
实际上它是一个普通的zip文件,您可以使用任何解压缩工具来查看其中的内容:
清单28.解压pod文件
root@computer:
~/fantom/lib/fan#unzip./hello.pod–d./hello
Archive:
./hello.pod
inflating:
./hello/meta.props
inflating:
./hello/fcode/names.def
inflating:
./hello/fcode/typeRefs.def
inflating:
./hello/fcode/methodRefs.def
inflating:
./hello/fcode/strs.def
inflating:
./hello/fcode