很多刚从 Java 转到 Scala 的开发者,第一次接触伴生对象时都会卡在一个问题上:既然 Scala 没有 static,那类级别的方法和属性到底该放哪。理解这个设计后,再看 apply() 和 case class,就会发现它们其实是一套连贯的语言机制。下面按“概念、调用、编译结果、典型场景”几个层次拆开说明,便于你快速建立判断标准。
为什么 Scala 没有 static
Scala 在语言设计上没有直接引入 static 关键字。常见的类级别属性、工具方法、程序入口等内容,通常放到 object 里定义。
可以先用一个便于迁移理解的说法:object 在很多场景下承担了 Java 里 static 的角色。
这并不意味着两者完全等价,但对初学者来说,这个映射足够帮助理解伴生对象为什么存在。
什么是伴生类和伴生对象
当同一个文件内同时存在同名的 class X 和 object X 时,这一对定义就构成了伴生关系:

class X是object X的伴生类object X是class X的伴生对象
这里有两个关键点:
1. 必须同名,通常配套出现
只有名字一致,才能形成标准的伴生关系。
2. 双方可以互相访问 private 成员
在 Scala 里,伴生类和伴生对象之间的边界比很多语言更弱,它们可以互相访问彼此的 private 字段和 private 方法。这也是伴生机制非常实用的原因之一。
编译结果与调用方式怎么理解
从编译结果看,伴生类和伴生对象并不是同一个东西,而是分别产出不同的字节码文件:
伴生类
class X会编译成X.class伴生对象
object X会编译成X$.class
从调用方式看,两者也有明显区别:
- 伴生对象中的属性或方法,可以直接通过类名调用
- 伴生类中的属性或方法,必须先创建实例后再调用
如果只有 object、没有对应的 class,通常可以把它理解为单例对象的写法。
基础语法示例
class AssociatedDemo {
val a:Int = 10;
var b:Int = 2;
}
object AssociatedDemo{
val object_v1:String = "变量1";
def main(args: Array[String]): Unit = {
// 注:只有伴生对象中可以定义主函数,类似于static修饰
println("伴生对象变量:" + AssociatedDemo.object_v1);
println("伴生类变量:" + (new AssociatedDemo()).a);
}
}
输出结果:
伴生对象变量:变量1
伴生类变量:10
这个例子适合用来记住最常见的分工:
object AssociatedDemo负责类级别访问入口new AssociatedDemo()负责实例级别状态
`apply()` 为什么常和伴生对象一起出现
apply() 是 Scala 很常见的一层语法糖。直接把类名或对象名当作函数来调用时,底层实际触发的就是对应的 apply() 方法。

这也是 Scala 更偏函数式风格的一部分:很多场景下,你可以把“创建对象”写得更像一次普通调用。
在伴生对象里用 apply() 创建实例
一个典型用法,是把构造逻辑放进伴生对象的 apply() 中,从而省掉显式的 new:
object AssociatedDemo {
def apply():AssociatedDemo = new AssociatedDemo();
def main(args: Array[String]): Unit = {
// 变量复制,直接可以实例化对象,不需要再new了
val ad1 = AssociatedDemo();
}
}
这里的:
val ad1 = AssociatedDemo()
本质上等价于调用:
AssociatedDemo.apply()
class 的 apply() 和 object 的 apply() 有什么区别
apply() 不只可以写在 object 中,也可以写在 class 中。区别在于调用目标不同:
class里的apply(),要先拿到实例,再通过实例调用object里的apply(),可以直接通过对象名调用
看下面这段代码就很清楚:
class AssociatedDemo {
// class中的apply()方法
def apply(param:String){
println("class apply method called:" + param)
}
}
object AssociatedDemo{
// object中的apply()方法
def apply(param:String){
println("object apply method called:" + param)
}
def main(args: Array[String]): Unit = {
// class 的apply()
val ad2 = new AssociatedDemo();
ad2("AAA")
ad2("BBB")
// object 的apply()
AssociatedDemo("CCC")
AssociatedDemo("DDD")
}
}
Demo 执行结果:
class apply method called:AAA
class apply method called:BBB
object apply method called:CCC
object apply method called:DDD
判断规则可以直接记成一句话:
ad2("AAA")调的是实例上的apply()AssociatedDemo("CCC")调的是伴生对象上的apply()
`case class` 和普通类有什么不同
case class(案例类)本质上仍然是类,但它更适合表示“以数据为中心、属性通常不可变”的对象。

case class中默认就带有apply(),因此创建对象时通常不需要写new。
原文把它类比为 Java 开发中的 entity 类,这种理解在入门阶段是有帮助的:它是一种更适合承载数据的特殊类定义形式。
按值比较,而不是按引用比较
case class 一个非常重要的特性,是对象比较更偏向“值语义”。示例如下:
case class Message(sender: String, recipient: String, body: String)
val message2 = Message("jorge@catalonia.es", "guillaume@quebec.ca", "Com va")
val message3 = Message("jorge@catalonia.es", "guillaume@quebec.ca", "Com va")
val messagesAreTheSame = message2 == message3 // true
虽然 message2 和 message3 引用的不是同一个对象,但因为它们的值相同,所以 message2 == message3 的结果是 true。
什么是 `case object`
没有参数的 case 形式,通常会写成 case object,而不是 case class。默认情况下,case object 是可序列化的。
case class CaseClass2(a:Int) extends SuperTrait // Case class
case object CaseObject extends SuperTrait // Case object
为什么 case class 适合配合 match 使用
case class 和 case object 经常与模式匹配 match 一起使用,这也是 Scala 中非常典型的写法:
// 定义案例类
abstract class Notification
case class Email(sender: String, title: String, body: String) extends Notification
case class SMS(caller: String, message: String) extends Notification
case class VoiceRecording(contactName: String, link: String) extends Notification
// match匹配对象类型
def showNotification(notification: Notification): String = {
notification match {
case Email(sender, title, _) => "Email"
case SMS(number, message) => "SMS"
case VoiceRecording(name, link) => "VoiceRecording"
}
}
这种写法的优势在于:数据结构和分支处理天然对应,代码读起来非常直接。
实际开发里怎么选
如果你只是想快速判断该用哪种写法,可以按下面的思路来分:
- 需要放类级别常量、工具方法、主函数入口:优先考虑
object - 需要表示可以实例化的对象状态:使用
class - 希望通过同名对象承载工厂方法、辅助方法:使用伴生对象
- 对象主要用于承载不可变数据,并且经常参与比较或模式匹配:优先考虑
case class
可以把它理解为一条渐进路径:先掌握伴生类和伴生对象的职责分工,再理解 apply() 的调用入口,最后把 case class 看成对这套机制的进一步封装。
参考资料
- Scala之:伴生对象与静态概念 - 掘金 (juejin.cn)
- 《Scala程序设计:Ja va虚拟机多核编程实战》
- 案例类(Case Classes) | Scala Documentation (scala-lang.org)







