仓颉之包 2024-09-02 周一

2024-09-01  本文已影响0人  勇往直前888

包的概述

包的声明

package pkg1      // root 包 pkg1
package pkg1.sub1 // root 包 pkg1 的子包 sub1
// file 1
// Comments are accepted
package test
// declarations...

// file 2
let a = 1 // Error, package declaration must appear first in a file
package test
// declarations...
  1. 包所在的文件夹名必须与包名一致。
  2. 源码根目录默认名为 src。
  3. 源码根目录下的包可以没有包声明,此时编译器将默认为其指定包名 default。
// The directory structure is as follows:
src
`-- directory_0
    |-- directory_1
    |    |-- a.cj
    |    `-- b.cj
    `-- c.cj
`-- main.cj
// a.cj
// in file a.cj, the declared package name must correspond to relative path directory_0/directory_1.

package default.directory_0.directory_1
// b.cj
// in file b.cj, the declared package name must correspond to relative path directory_0/directory_1.

package default.directory_0.directory_1
// c.cj
// in file c.cj, the declared package name must correspond to relative path directory_0.

package default.directory_0
// main.cj
// file main.cj is in the module root directory and may omit package declaration.

main() {
    return 0
}
// a.cj
package a
public class B { // Error, 'B' is conflicted with sub-package 'a.B'
    public static func f() {}
}

// b.cj
package a.B
public func f {}

// main.cj
import a.B // ambiguous use of 'a.B'

main() {
    a.B.f()
    return 0
}

顶层声明的可见性

  1. private 表示仅当前文件内可见。不同的文件无法访问这类成员。

  2. internal 表示仅当前包及子包(包括子包的子包)内可见。同一个包内可以不导入就访问这类成员,当前包的子包(包括子包的子包)内可以通过导入来访问这类成员。

  3. protected 表示仅当前模块内可见。同一个包的文件可以不导入就访问这类成员,不同包但是在同一个模块内的其它包可以通过导入访问这些成员,不同模块的包无法访问这些成员。

  4. public 表示模块内外均可见。同一个包的文件可以不导入就访问这类成员,其它包可以通过导入访问这些成员。

  1. pacakge 支持使用 internal、protected、public,默认修饰符为 public。

  2. import 支持使用全部访问修饰符,默认修饰符为 private。

  3. 其他顶层声明支持使用全部访问修饰符,默认修饰符为 internal。

package a

private func f1() { 1 }   // f1 仅在当前文件内可见
func f2() { 2 }           // f2 仅当前包及子包内可见
protected func f3() { 3 } // f3 仅当前模块内可见
public func f4() { 4 }    // f4 当前模块内外均可见
  1. 函数声明中的参数与返回值
// a.cj
package a
class C {}
public func f1(a1: C) // Error, public declaration f1 cannot use internal type C.
{
    return 0
}
public func f2(a1: Int8): C // Error, public declaration f2 cannot use internal type C.
{
    return C()
}
public func f3 (a1: Int8) // Error, public declaration f3 cannot use internal type C.
{
    return C()
}
  1. 变量声明
// a.cj
package a
class C {}
public let v1: C = C() // Error, public declaration v1 cannot use internal type C.
public let v2 = C() // Error, public declaration v2 cannot use internal type C.
  1. 类声明中继承的类
// a.cj
package a
open class C1 {}
public class C2 <: C1 {} // Error, public declaration C2 cannot use internal type C1.
  1. 类型实现的接口
// a.cj
package a
interface I {}
public enum E <: I { A } // Error, public declaration uses internal types.
  1. 泛型类型的类型实参
// a.cj
package a
public class C1<T> {}
class C2 {}
public let v1 = C1<C2>() // Error, public declaration v1 cannot use internal type C2.
  1. where 约束中的类型上界
// a.cj
package a
interface I {}
public class B<T> where T <: I {}  // Error, public declaration B cannot use internal type I.
// a.cj
package a
class C1 {}
func f1(a1: C1)
{
  return 0
}
public func f2(a1: Int8) // Ok.
{
  var v1 = C1()
  return 0
}
public let v1 = f1(C1()) // Ok.
public class C2 // Ok.
{
  var v2 = C1()
}
public var t1: () -> Unit = { => } // Ok.
func f1(): Unit {}
public let t2 = f1 // Ok.

public func f2() // Ok.
{
  return f1
}
var num = 5
public var t3 = num // Ok.

包的导入

使用 import 语句导入其它包中的声明或定义

package a
import std.math.*
import package1.foo
import {package1.foo, package2.bar}
import package1.{foo, bar, fuzz}

等价于

import package1.foo
import package1.bar
import package1.fuzz
import package1.*
import {package1.*, package2.*}
  1. import 可以被 private、internal、protected、public 访问修饰符修饰。不写访问修饰符的 import 等价于 private import。

  2. 导入的成员的作用域级别低于当前包声明的成员。

  3. 当已导出的包的模块名或者包名被篡改,使其与导出时指定的模块名或包名不一致,在导入时会报错。

  4. 只允许导入当前文件可见的顶层声明或定义,导入不可见的声明或定义将会在导入处报错。

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

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

// pkga/a.cj
package pkga    // Error, packages pkga pkgb are in circular dependencies.
import pkgb.*

class C {}
public struct R {}

// pkgb/b.cj
package pkgb

import pkga.*

// pkgc/c1.cj
package pkgc

import pkga.C // Error, 'C' is not accessible in package 'pkga'.
import pkga.R // OK, R is an external top-level declaration of package pkga.
import pkgc.f1 // Error, package 'pkgc' should not import itself.

public func f1() {}

// pkgc/c2.cj
package pkgc

func f2() {
    /* OK, the imported declaration is visible to all source files of the same package
     * and accessing import declaration by its name is supported.
     */
    R()

    // OK, accessing imported declaration by fully qualified name is supported.
    pkga.R()

    // OK, the declaration of current package can be accessed directly.
    f1()

    // OK, accessing declaration of current package by fully qualified name is supported.
    pkgc.f1()
}
// pkga/a.cj
package pkga

public struct R {}            // R1
public func f(a: Int32) {}    // f1
public func f(a: Bool) {} // f2

// pkgb/b.cj
package pkgb
import pkga.*

func f(a: Int32) {}         // f3
struct R {}                 // R2

func bar() {
    R()     // OK, R2 shadows R1.
    f(1)    // OK, invoke f3 in current package.
    f(true) // OK, invoke f2 in the imported package
}

隐式导入 core 包

诸如 String、Range 等类型能直接使用,并不是因为这些类型是内置类型,而是因为编译器会自动为源码隐式的导入 core 包中所有的 public 修饰的声明。

使用 import as 对导入的名字重命名

  1. 使用 import as 对导入的声明进行重命名后,当前包只能使用重命名后的新名字,原名无法使用。

  2. 如果重命名后的名字与当前包顶层作用域的其它名字存在冲突,且这些名字对应的声明均为函数类型,则参与函数重载,否则报重定义的错误。

  3. 支持 import pkg as newPkgName 的形式对包名进行重命名,以解决不同模块中同名包的命名冲突问题。

// a.cj
package p1
public func f1() {}

// d.cj
package p2
public func f3() {}

// b.cj
package p1
public func f2() {}

// c.cj
package pkgc
public func f1() {}

// main.cj
import p1 as A
import p1 as B
import p2.f3 as f  // OK
import pkgc.f1 as a
import pkgc.f1 as b // OK

func f(a: Int32) {}

main() {
    A.f1()  // OK, package name conflict is resolved by renaming package name.
    B.f2()  // OK, package name conflict is resolved by renaming package name.
    p1.f1() // Error, the original package name cannot be used.
    a()     // Ok.
    b()     // Ok.
    pkgc.f1()    // Error, the original name cannot be used.
}
// a.cj
package p1
public class C {}

// b.cj
package p2
public class C {}

// main1.cj
package pkga
import p1.C
import p2.C

main() {
    let _ = C() // Error
}

// main2.cj
package pkgb
import p1.C as C1
import p2.C as C2

main() {
    let _ = C1() // Ok
    let _ = C2() // Ok
}

// main3.cj
package pkgc
import p1
import p2

main() {
    let _ = p1.C() // Ok
    let _ = p2.C() // Ok
}

重导出一个导入的名字

在仓颉编程语言中,import 可以被 private、internal、protected、public 访问修饰符修饰。其中,被 public、protected 或者 internal 修饰的 import 可以把导入的成员重导出(如果这些导入的成员没有因为名称冲突或者被遮盖导致在本包中不可用)。其它包可以根据可见性直接导入并使用本包中用重导出的内容,无需从原包中导入这些内容。

package a

public let x = 0
public import a.b.f
internal package a.b

public func f() { 0 }
import a.f  // Ok
let _ = f() // Ok
public import a.b // Error, cannot re-export package

程序入口

// main.cj
main(): Int64 { // Ok.
    return 0
}
// main.cj
main(args: Array<String>): Unit { // Ok.
    for (arg in args) {
        println(arg)
    }
}

/* 使用 cjc main.cj 编译完成后,通过命令行执行:./main Hello, World,将会得到如下输出:
Hello,
World
*/
// main.cj
main(): String { // Error, return type of 'main' is not 'Integer' or 'Unit'.
    return ""
}
// main.cj
main(args: Array<Int8>): Int64 { // Error, 'main' cannot be defined with parameter whose type is not Array<String>.
    return 0
}
// main.cj
// Error, multiple 'main's are found in source files.
main(args: Array<String>): Int32 {
    return 0
}

main(): Int8 {
    return 0
}
上一篇 下一篇

猜你喜欢

热点阅读