仓颉编程语言青少年基础教程:包(Package)

在仓颉编程语言中,程序以包(Package)的形式进行组织,包是最小的编译单元。包可以定义子包,从而构成树形结构。没有父包的包称为 root(根)包,root 包及其子包(包括子包的子包)构成的整棵树称为模块(module)。一个包中可以包含多个源文件。

包路径应与文件系统目录结构一致。

模块是若干包的集合,是第三方开发者发布的最小单元。一个模块的程序入口只能在其根目录下,它的顶层最多只能有一个作为程序入口的 main ,该 main 没有参数或参数类型为 Array<String>,返回类型为整数类型或 Unit 类型。

【在 CodeArts IDE for Cangjie 中,当你新建一个工程(Project:工程/项目)时,你为工程所起的名称(Project Name),就会作为该工程的 root(根)包(Package)的名称,根包名一旦确定不要随意修改。root 包及其子包(包括子包的子包)构成的整棵树称为模块(module)。

在 CodeArts IDE for Cangjie 中新建工程时,你为工程指定的 Project Name 会被同时用作:

  • 工程根目录名(Project Name)
  • root 包名
  • 该 root 包及其所有子包所组成的 模块(module 的名称 】

基本概念关键区别表

概念

定义

特点

源文件

.cj 文件

物理文件,必须声明所属包

编译最小单元

可含多个源文件,独立命名空间,独立编译

模块

包的集合

发布单元,可含多个包,最多一个main入口

包声明:每个文件必须声明package,位于文件开头(包声明必须在源文件的非空非注释的首行),由点分隔的层级结构,包名各段必须是一个仅含 ASCII 字符的合法的普通标识符,推荐用小写字母。

注意:当前 Windows 平台版本,包名暂不支持使用 Unicode 字符,包名各段必须是一个仅含 ASCII 字符的合法的普通标识符。

包由一个或多个源码文件组成,同一个包的源码文件必须在同一个目录。一个包中可以包含多个源文件。模块(module)是若干包(Package)的集合。在仓颉编程语言中,一个模块根目录下的顶层最多只能有一个作为程序入口的 main 函数。

包的导入使用import

在仓颉编程语言中,可以通过 import fullPackageName.itemName 的语法导入其他包中的一个顶层声明或定义,fullPackageName 为完整路径包名,itemName 为声明的名字。

如果要导入的多个 itemName 同属于一个 fullPackageName,可以使用 import fullPackageName.{itemName1, itemName2, …} 语法

还可以使用 import packageName.* 语法将 packageName 包中所有可见的顶层声明或定义全部导入。

导入语法

导入场景

语法示例

说明

导入单个声明

import pkg1.foo

导入pkg1包中的foo声明

导入多个声明(同包)

import pkg1.{foo, bar}

批量导入pkg1包的foo和bar

导入包内所有可见声明

import pkg1.*

导入pkg1包中符合访问权限的所有声明

导入多个包的所有声明

import {pkg1.*, pkg2.*}

批量导入多个包的所有可见声明

几点说明:

  • 隐式导入:编译器自动导入core包的所有public声明(如String、Range等常用类型,无需手动导入)。

  •同名标识符怎么导入使用?当不同包存在同名声明时,可通过重命名避免冲突。语法:

import 包名.声明名 as 新名 或 import 包名 as 新包名。

重命名后,当前包只能使用重命名后的新名字,原名无法使用。

  • 作用域优先级:导入的声明作用域低于当前包的声明,同名声明会被 “遮盖”(函数重载除外)。

  • 禁止通过 import 导入当前源文件所在包的声明或定义。

  • 禁止包间的循环依赖导入,如果包之间存在循环依赖,编译器会报错。

示例

目录结构如下:

 demoA                  ← 模块根(root)目录——工程(Project)名称
  └── src                ← 源码根目录(名字必须是 src)
          ├── main.cj         ← 模块入口
          ├── c.cj           
          └── directory_0    ← 包 demoA.directory_0
                  ├── a.cj
                  ├── c.cj        
                  └── directory_1 ← 包 demoA.directory_0.directory_1                
                          └── b.cj  

源文件 

(1) src/directory_0/directory_1/b.cj文件内容:

package demoA.directory_0.directory_1

public func helloB() {
    println("demoA.directory_0.directory_1 中 b.cj文件的helloB()")
}

(2) src/directory_0/a.cj文件内容:

package demoA.directory_0 

public func helloA() {
    println("demoA.directory_0 中 a.cj文件的helloA()")
}

public func helloB() {
    println("demoA.directory_0 中 a.cj文件的helloB()")
}

(3) src/directory_0/c.cj文件内容:

package demoA.directory_0   // 首行必须是 package 声明

public func helloC() {
    println("demoA.directory_0 中 c.cj文件的helloC()")
}

(4) src/c.cj文件内容:

package demoA

public func helloC() {
    println("demoA.directory_0 中 c.cj文件的helloC()")    
}

(5) src/main.cj文件内容:

package demoA

import demoA.directory_0.*
import demoA.directory_0.directory_1.helloB as helloB2
import demoA.directory_0.helloC as helloC2

main(): Int64 {
    println("hello world")    

    helloC()   
    helloA()
    helloB()

    helloC2()
    helloB2()
    return 0
}

编译运行截图:

【顺便提示,如何在CodeArts IDE for Cangjie 创建的工程(Project) 源码根目录src上新建源码文件或子目录?

右击src可见相关快捷菜单命令项。】

请对输出

hello world
demoA.directory_0 中 c.cj文件的helloC()
demoA.directory_0 中 a.cj文件的helloA()
demoA.directory_0 中 a.cj文件的helloB()
demoA.directory_0 中 c.cj文件的helloC()
demoA.directory_0.directory_1 中 b.cj文件的helloB()

仔细分析体会。

分析提示

1.目录层级与包声明完全一致

demoA/src/directory_0/                → package demoA.directory_0

demoA/src/directory_0/directory_1/    → package demoA.directory_0.directory_1

2.同名符号分布

helloB 同时出现在

– demoA.directory_0(a.cj)

– demoA.directory_0.directory_1(b.cj)

helloC 同时出现在

– demoA(c.cj)

– demoA.directory_0(c.cj)

3.导入方式

 • import demoA.directory_0.*

把 demoA.directory_0 里所有可见符号一次性导入。

因为 demoA.directory_0 和 demoA 都有 helloC,为了避免歧义,所以在后面又单独写了一句:

import demoA.directory_0.helloC as helloC2 —— 显式起别名区分。

 • helloB 同理:通过 import … as helloB2 显式起别名区分。

4. 仓颉语言的源码根目录src本身不会出现在包路径和import路径里,写上将出错,它只是文件系统的边界标记,而不是逻辑命名空间的一部分——这一点和Java语言类似。仓颉的项目名(模块名)必须出现在源码路径的最外层——这一点和Java语言不同。

例如,前面提到的示例中

src/directory_0/a.cj文件内容第一句

package demoA.directory_0 ,不能写为package demoA.src.directory_0

并且,在其它代码文件中如 模块入口文件main.cj 导入这个文件中的函数使用时

import demoA.directory_0.* ,不能写为import demoA.src.directory_0.*

另一个仓颉语言包示例:演示包结构和访问修饰符的使用  https://blog.csdn.net/cnds123/article/details/150225446

相关官方文档

https://cangjie-lang.cn/docs?url=%2F1.0.0%2Fuser_manual%2Fsource_zh_cn%2Fpackage%2Fpackage_overview.html

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐