Package Import
Using the import Statement to Import Declarations or Definitions from Other Packages
In Cangjie, you can use the import fullPackageName.itemName
syntax to import a top-level declaration or definition from another package into a source file. That imported package member may then be used by its name throughout the file, except for scopes where its name is shadowed by another declaration or definition, if any. fullPackageName
is the full path package name, and itemName
is the name of the imported package member. Import declarations must be placed after the package declaration and before other declarations or definitions in the source file. For example:
package a
import std.math.*
import package1.foo
import {package1.foo, package2.bar}
If multiple itemName
to be imported belong to the same fullPackageName
, you can use the import fullPackageName.{itemName[, itemName]*}
syntax. For example:
import package1.{foo, bar, fuzz}
This is equivalent to:
import package1.foo
import package1.bar
import package1.fuzz
In addition to importing a specific top-level declaration or definition using the import fullPackagename.itemName
syntax, you can also use the import packageName.*
syntax to import all visible top-level declarations and definitions of the packageName
package. For example:
import package1.*
import {package1.*, package2.*}
Note:
import
can be modified by an access modifierprivate
,internal
,protected
, orpublic
(see Re-exporting an Imported Name below). Animport
without an access modifier is equivalent to aprivate import
.- The scope level of imported members is lower than that of members declared in the current package.
- If the module name or package name of an exported package is tampered with, causing it to differ from the name specified at the time of export, an error will occur during the import.
- Only top-level declarations and definitions visible to the current file can be imported. Importing invisible declarations or definitions will result in an error at the import location.
- As there is no need to import the declarations and definitions from the package to which the current source file belongs, the compiler treats such import as an error.
import
is not allowed for packages with cyclic dependency. If a cyclic dependency exists between packages, the compiler reports an error.
The following is an example:
// 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()
}
In Cangjie, an imported declaration or definition that has the same name as a top-level declaration or definition in the current package will be shadowed unless it constitutes an overloaded function. If it does constitute an overloaded function, function resolution during a function call follows the normal rules for overloaded functions.
// 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
}
Importing the core Package in Implicit Mode
Types such as String
and Range
can be used directly not because they are built into the language, but because the compiler automatically imports all public
declarations from the core
package into all source files in implicit mode.
Using "import as" to Rename the Imported File
Namespaces of different packages are separate, so it is possible to have top-level declarations with the same name in different packages. When importing multiple top-level declarations with the same name from different packages, you can use import packageName.name as newName
to rename all but one of them to avoid conflicts. (Of course, you can also use import as
for renaming even if there is no name conflict.) The rules for import as
are as follows:
-
After you use
import as
to rename an imported entity, the current package can use only the new name. -
If the new names conflict with names of members of the current package, and declarations corresponding to these names are functions, they are involved in function overloading; otherwise, a redefinition error occurs.
-
Packages can be renamed in
import pkg as newPkgName
format to solve the name conflicts of packages with the same name in different modules.// 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. }
-
If conflicting imported packages are not renamed, no error will occur in the
import
statement. However, an error will be reported during usage because the imported names are not unique. In this case, you can useimport as
to define an alias or useimport fullPackageName
to import a package as the namespace.// 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 }
Re-exporting an Imported Name
During the development of a large-scale project with various functionalities, the following scenario is common: Package p2
uses a large number of declarations imported from package p1
. For a package p3
to import package p2
and use its functions, the declarations from package p1
must also be visible to package p3
. If p3
is required to import the declarations of p1
that are used in p2
, the process becomes cumbersome. Therefore, it is desirable to simultaneously import package p2
and the declarations of p1
that are used in p2
.
In Cangjie, import
can be modified by the access modifiers private
, internal
, protected
, and public
. An import
modified by public
, protected
, or internal
can re-export the imported members (unless the imported members are unavailable in the package due to name conflicts or being shadowed). Other packages can directly import and use the entities re-exported by the current package according to visibility, without the need to import it from the original package.
private import
: The imported content can be accessed only in the current file.private
is the default modifier ofimport
. Animport
without an access modifier is equivalent to aprivate import
.internal import
: The imported content can be accessed in the current package and its subpackages (including nested subpackages). Explicitimport
is required for non-current packet access.protected import
: The imported content can be accessed in the current module. Explicitimport
is required for non-current packet access.public import
: The imported content can be accessed externally. Explicitimport
is required for non-current packet access.
In the following example, b
is a subpackage of a
, and a
re-exports the f
function that is defined in b
through public 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
It is worth noting that entire packages cannot be re-exported. If a package is imported by an import
, the import
cannot be modified by public
, protected
, or internal
.
public import a.b // Error, cannot re-export package