仓颉语言顶层声明可见性全解析:private / internal / protected / public 访问控制实战指南

发布时间:2026/10/15 15:12:08
仓颉语言顶层声明可见性全解析:private / internal / protected / public 访问控制实战指南 文档编程语言【免费下载链接】cangjie_docs仓颉编程语言开发者文档。项目地址https://gitcode.com/Cangjie/cangjie_docs点击查看免费下载本文以仓颉编程语言官方开发者文档《顶层声明的可见性》为主体系统讲解作用于类型、变量、函数等顶层声明的四种访问修饰符private、internal、protected、public的语义边界、默认规则与编译期校验。阅读完本文你将能够独立规划包与模块的对外暴露面正确设计库的公开 API并规避公开声明使用低可见性类型等典型编译错误。前置概念包、模块与顶层声明在理解访问修饰符之前需要先明确仓颉语言中两个核心的组织单元包package是仓颉的编译最小单元。每个包可以单独输出 AST 文件、静态库文件、动态库文件等产物每个包有自己的名字空间同一个包内不允许有同名的顶层定义或声明函数重载除外一个包可包含多个源文件且同一个包的源码文件必须在同一目录。详见 包的概述。模块module是若干包的集合是第三方开发者发布的最小单元。没有父包的包称为 root 包root 包及其子包包括子包的子包构成的整棵树即为一个模块。模块的程序入口只能位于其根目录下顶层最多只能有一个作为程序入口的main。详见 包和模块管理。在仓颉的顶层作用域源文件中不被任何大括号包裹的最外层作用域中可以定义变量、函数和自定义类型如struct、class、enum和interface等这些统称为顶层声明。其中变量和函数分别被称为全局变量和全局函数main则是编译为可执行文件时的程序入口。相关背景可参考 程序结构。由于包是编译单元、模块是发布单元访问修饰符的可见性范围正是围绕文件 → 包及子包 → 模块 → 所有包这四个层级展开的。四种访问修饰符的顶层语义仓颉语言有 4 种访问修饰符private、internal、protected、public它们都是语言关键字见 keyword 文档。当用于修饰顶层元素时各修饰符的语义如下private仅当前文件内可见。不同的文件无法访问这类成员。internal仅当前包及子包包括子包的子包内可见。同一个包内可以不导入就访问这类成员当前包的子包包括子包的子包内可以通过导入来访问这类成员。protected仅当前模块内可见。同一个包的文件可以不导入就访问这类成员不同包但同属一个模块内的其他包可以通过导入访问这些成员不同模块的包无法访问这些成员。public模块内外均可见。同一个包的文件可以不导入就访问这类成员其他包可以通过导入访问这些成员。四种修饰符的可见范围可汇总为下表修饰符文件包及子包模块所有包privateYNNNinternalYYNNprotectedYYYNpublicYYYY可以这样理解这张表的读法private的可见范围最小仅限单个文件internal将范围放宽到当前包及其子包同一包内免导入子包需导入protected进一步放宽到整个模块同包免导入同模块其他包需导入public则完全对外开放同包免导入其他包需导入。可见性范围是逐级递增的包含关系。一个直观的代码示例摘自原文档可正常编译package a private func f1() { 1 } // f1 仅在当前文件内可见 func f2() { 2 } // f2 仅当前包及子包内可见 protected func f3() { 3 } // f3 仅当前模块内可见 public func f4() { 4 } // f4 当前模块内外均可见注意上例中f2未写任何访问修饰符它采用的是其他顶层声明的默认修饰符internal详见下一节因此其可见范围是当前包及子包。与 class 成员访问修饰符的语义差异顶层声明的protected语义仅模块内可见与class成员的protected语义模块内可见 当前类子类中可见存在差异二者的适用语境不同不可混用。class成员包括成员变量、成员属性、构造函数、成员函数同样支持这四种访问修饰符缺省含义为internal其中protected除了在模块内可见外还允许当前类的子类访问。详见 class 成员的访问修饰符。不同顶层声明支持的修饰符与默认修饰符并非所有顶层声明都支持全部四种访问修饰符仓颉对三类顶层声明分别规定了可用的修饰符集合与默认修饰符默认修饰符指省略修饰符时的语义这些默认修饰符也允许显式写出package支持使用internal、protected、public默认修饰符为public。注意package声明本身不支持private——包声明是文件所属命名空间的标识其可见性天然与包名绑定。import支持使用全部访问修饰符默认修饰符为private。这意味着不写修饰符的import等价于private import导入的内容仅在当前文件内可访问。其他顶层声明函数、变量、自定义类型等支持使用全部访问修饰符默认修饰符为internal。上例中的func f2()即属此类。顶层声明类别支持的修饰符默认修饰符packageinternal、protected、publicpublicimportprivate、internal、protected、publicprivate其他顶层声明private、internal、protected、publicinternal默认修饰符对库设计的影响默认修饰符的组合规则对日常开发有直接影响顶层函数、全局变量、自定义类型默认是internal即默认只对当前包及其子包可见。如果希望被其他包导入使用必须显式写出public。这是仓颉与许多语言默认公开策略的重要区别。包声明默认public与 包名声明 中包声明必须出现在源文件非空非注释的首行的语法要求配合使用。import默认private若希望将导入的内容传递给其他包使用需要显式使用internal、protected或public修饰即重导出。例如public import a.b.f可以把子包b中定义的函数f通过包a重导出使外部只需import a.f即可使用。详见 包的导入。访问级别排序与声明不得高于所用类型规则仓颉的访问级别排序为public protected internal private。一个核心约束是一个声明的访问修饰符不得高于该声明中用到的类型的访问修饰符的级别。这保证了对外可见的声明不会泄露内部不可见的类型——否则外部使用者将无法完整引用该声明的签名。以下四类场景均受此规则约束以下示例均来自原文档编译均报错函数声明中的参数与返回值// 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() }上例中class C未加修饰符默认级别为internal而f1、f2、f3均为public它们的形参类型或返回类型用到了C即public 声明使用了 internal 类型编译器据此报错。注意f3的形参和返回类型都是Int8仅仅因为函数体内部构造并返回了C()同样触发了该错误——这与下文public 声明在函数体内可以使用本包任意类型的规则需要仔细区分签名参数/返回值中直接出现的类型受级别约束而函数体内局部使用的类型不受签名级别约束。变量声明// 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.显式标注类型v1和依赖类型推断v2的public变量声明只要其类型最终落到internal级别都会编译报错。泛型类型的类型实参// a.cj package a public class C1T {} class C2 {} public let v1 C1C2() // Error, public declaration v1 cannot use internal type C2.public声明的变量以internal级别的C2作为泛型实参实例化C1C2同样违规。where约束中的类型上界// a.cj package a interface I {} public class BT where T : I {} // Error, public declaration B cannot use internal type I.where约束中的类型上界也属于声明中用到的类型public类的泛型约束若引用internal级别的接口I编译器同样报错。这与 扩展的导入导出 中扩展是否导出由被扩展类型与泛型约束的访问修饰符共同决定的规则同源泛型约束的可见性直接决定依赖该约束的声明能否对外暴露。规则的本质上述规则的共同本质是对外暴露的声明签名中出现的每一个类型含泛型实参、约束上界其可见级别都必须不低于该声明本身。在设计库的公开 API 时应优先确保公开类型及其依赖类型均为public或将使用低可见性类型的声明降级为internal/protected。public 声明在使用上的灵活空间与签名类型的严格约束相对仓颉为public声明预留了充分的实现自由度。以下场景全部合法可编译通过函数体与初始化表达式可用本包任意类型public修饰的声明在其初始化表达式或者函数体里面可以使用本包可见的任意类型包括被public修饰的类型和不被public修饰的类型// 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() }这里f2的签名中只出现Int8函数体内局部使用internal级别的C1是允许的public let v1的初始化表达式调用internal级别的f1并传入C1()也是允许的public class C2的成员变量v2使用C1同样 OK——成员的类型不在顶层声明签名的检查范围内。匿名函数与任意顶层函数public修饰的顶层声明能使用匿名函数或者任意顶层函数包括被public修饰和不被public修饰的顶层函数public var t1: () - Unit {} // OK. func f1(): Unit {} public let t2 f1 // OK. public func f2() // OK. { return f1 }public var t1以匿名函数初始化、public let t2引用internal级别的顶层函数f1、public func f2的函数体返回f1均为合法用法。函数作为一等公民可以被自由引用其可见级别不构成对public声明的约束。内置类型默认是 public内置类型诸如Rune和Int64等默认的修饰符是public。因此基于内置类型的public声明天然满足声明级别不高于所用类型级别的约束var num 5 public var t3 num // OK.这也解释了为什么几乎所有公开 API 都可以放心使用Int64、String、Bool等内置类型作为形参和返回值。此外String、Range等类型能直接使用还因为编译器会自动为源码隐式导入core包中所有public修饰的声明详见 包的导入 的隐式导入 core 包一节。同包内 private 同名自定义类型的编译期限制限制的由来同一个包内private修饰的同名自定义类型如struct、class、enum和interface等在某些场景下不支持不支持场景由编译器进行报错。这与包内不允许同名顶层定义的一般规则形成补充private类型因可见范围仅限单文件本可在不同文件中各自独立存在但当它们被包内其他公开可见的声明间接导出时会在产物层面产生名字冲突。复现示例例如在以下程序中example1.cj与example2.cj文件包名相同均为package test在example1.cj文件中定义了private修饰的类A在example2.cj文件中定义了private修饰的结构体A// example1.cj package test private class A {} public class DT { private let a: A A() }// example2.cj package test private struct A {} public class CT { private let a: A A() }运行以上程序将输出error: currently, it is not possible to export two private declarations with the same name规避建议该报错的关键在于两个private同名类型都被包内的public泛型类以成员形式间接承载编译器在导出产物时无法区分两者。规避手段包括在同一包内避免使用同名的private自定义类型即使它们位于不同文件若确需同名应至少将其中的一个提升为internal或更高的可见级别或改用不同的标识符若仅需在单文件内使用可考虑将类型定义与使用方放在同一文件中以隔离冲突面。与导入 / 重导出机制的联动顶层声明的可见性直接决定了import的可用性。仓颉的导入规则详见 包的导入包括只允许导入当前文件可见的顶层声明或定义导入不可见的声明或定义会在导入处报错禁止通过import导入当前源文件所在包的声明或定义禁止包间的循环依赖导入若存在循环依赖编译器会报错。结合可见性语义可以得到完整的使用准则同一包内的internal/protected/public成员可直接访问免导入private成员仅限本文件子包访问父包或模块内其他包的internal/protected成员必须显式import跨模块只能访问public成员同样需要显式import若希望把从别处导入的成员继续传递给下游包使用应使用public import/protected import/internal import进行重导出但包本身不可被重导出public import a.b会报错。编译验证与实战建议用 cjc 快速验证以上所有代码示例均可使用仓颉编译命令cjc验证。cjc的基本用法为cjc [option] file...默认将给定源代码文件编译为名为main的可执行文件详见 cjc 使用cjc a.cj对于多文件、多包的工程可配合cjpm进行整包编译与产物管理编译选项细节参见 compile_options。面向库作者的检查清单在编写将被其他包或模块消费的仓颉代码时建议按以下清单自查默认意识未写修饰符的顶层函数/变量/类型是internal仅包及子包可见需要对外发布时显式写public。签名级别public声明的形参、返回值、泛型实参、where约束上界中出现的每个类型级别都不得低于public。实现自由度public声明的函数体、初始化表达式内部可以放心使用本包任意级别含private、internal的类型与函数。同名冲突同一包内避免多个private同名自定义类型被公开声明间接导出。导入传递需要把导入的成员传递给下游时用带访问修饰符的import重导出默认的private import只对本文件可见。模块边界protected与internal都不可跨越模块边界跨模块只能依赖public。扩展场景的关联提醒顶层声明可见性同时是 扩展的导入导出 规则的基石直接扩展是否导出由被扩展类型与泛型约束的访问修饰符共同决定当所有泛型约束都是导出类型时该扩展才被导出。在package a.b中private interface I1、internal interface I2、protected interface I3会分别导致基于它们的扩展对包外、子包外不可见。设计扩展 API 时需要与顶层声明的可见性一并规划。综上仓颉的顶层声明访问控制以文件 → 包及子包 → 模块 → 所有包为坐标系通过四种访问修饰符精确划分了每一层声明的可见边界再辅以声明级别不得高于所用类型级别的编译期校验从语言层面保障了模块封装与 API 安全。掌握本文的语义表、默认修饰符规则与典型错误模式即可在仓颉工程中游刃有余地设计包内实现细节与对外公开接口。赞分享文档编程语言【免费下载链接】cangjie_docs仓颉编程语言开发者文档。项目地址https://gitcode.com/Cangjie/cangjie_docs点击查看免费下载相关推荐仓颉语言顶层声明可见性详解public/protected/internal/private 四种访问修饰符的语义、默认规则与编译约束仓颉语言顶层声明可见性详解public/protected/internal/private 四种访问修饰符的语义、默认规则与编译约束 仓颉Cangjie文档编程语言Swift 访问控制完全指南用 open、public、package、internal、fileprivate、private 精确管理代码可见性Swift 访问控制完全指南用 open、public、package、internal、fileprivate、private 精确管理代码可见性 导读 访文档教程Slang 声明可见性完全解析public / internal / private 的判定规则、语言版本默认值与过滤时机Slang 声明可见性完全解析public / internal / private 的判定规则、语言版本默认值与过滤时机 本文以 Slang 官方设计文档编译器图形学编程语言创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询