scala类和对象
Scala 类和对象:面向对象的简洁实现
Scala 作为一门纯粹的面向对象语言,其类和对象的设计既保留了面向对象的核心思想,又通过语法糖简化了代码编写,相比 Java 更加简洁灵活。本文将深入解析 Scala 类和对象的定义、属性访问机制及底层实现。
类的定义与特性
基本语法
Scala 中类的定义无需显式声明 public(默认即为公有),一个源文件可包含多个类,所有类均具有公有可见性。
// 语法格式
[修饰符] class 类名 {
// 类体(属性、方法等)
}
示例:定义一个简单的类
class Cat {
// 用var声明可变属性(会生成getter和setter)
var name: String = _ // _表示赋默认值(需指定类型)
var color: String = _
// 用val声明不可变属性(仅生成getter)
val life: Int = 10 // val必须显式赋值,且不可修改
}
代码解析:
- 属性初始化:Scala 要求类的属性必须显式初始化。若暂时不想赋值,可用
_赋默认值(需指定类型),默认值规则与 Java 基本类型一致(如字符串默认null,整数默认0)。 var与val的区别:var:可变属性,编译后会生成 getter(获取值)和 setter(修改值)方法。val:不可变属性,编译后仅生成 getter 方法(无 setter,因为值不可修改)。
对象的实例化与使用
实例化对象
Scala 实例化对象的语法与 Java 类似,但可省略 new 后的括号(无参构造时)。
object CatDemo {
def main(args: Array[String]): Unit = {
// 实例化Cat对象(无参构造可省略括号)
val cat = new Cat() // 或简写为 new Cat
// 访问和修改属性(实际调用getter和setter)
cat.name = "小花" // 调用setter方法
cat.color = "黄白" // 调用setter方法
// 打印属性(实际调用getter方法)
println(s"${cat.name}的颜色是${cat.color},寿命为${cat.life}年")
}
}
输出结果:
小花的颜色是黄白,寿命为10年
底层实现:从 Scala 到 Java 的转换
Scala 代码最终会编译为 JVM 字节码,通过反编译可清晰看到类的底层实现。以上述 Cat 类为例,编译后生成的 Java 代码大致如下:
public class Cat {
// 属性默认是private
private String name;
private String color;
private final int life; // val修饰的属性是final
// 无参构造方法
public Cat() {
this.life = 10; // val属性在构造时初始化
}
// var属性的getter(name())
public String name() {
return this.name;
}
// var属性的setter(name_$eq(),Scala的特殊命名)
public void name_$eq(String x$1) {
this.name = x$1;
}
// color的getter和setter(与name类似)
public String color() {
return this.color;
}
public void color_$eq(String x$1) {
this.color = x$1;
}
// val属性的getter(仅读取,无setter)
public int life() {
return this.life;
}
}
关键发现:
- 属性访问的本质:
Scala 中cat.name = "小花"并非直接修改私有属性,而是调用了编译器生成的name_$eq("小花")方法(setter);cat.name则调用了name()方法(getter)。这种语法糖让代码更简洁。 val的不可变性:val life: Int = 10编译后为private final int life,且无 setter 方法,确保值无法修改。- 构造方法:
类体中val life: Int = 10的初始化逻辑会被放入构造方法中执行。
类的访问修饰符
Scala 支持多种访问修饰符,控制属性和方法的可见性,与 Java 略有差异:
| 修饰符 | 含义 | 对比 Java |
|---|---|---|
| 无修饰符 | 同包可见(默认) | Java 默认是包内可见 |
private |
仅当前类可见(包括伴生对象) | Java 的 private 是类内可见 |
protected |
仅当前类及子类可见(同包子类不可见) | Java 的 protected 允许同包访问 |
private[this] |
仅当前实例可见(更严格的 private) | 无对应修饰符 |
示例:使用 private 修饰符
class Dog {
private var age: Int = 3 // 仅Dog类及伴生对象可见
def getAge(): Int = age // 类内可访问
}
object Dog {
def printAge(dog: Dog): Unit = {
println(dog.age) // 伴生对象可访问private属性
}
}
// 测试
object DogDemo {
def main(args: Array[String]): Unit = {
val dog = new Dog()
println(dog.getAge()) // 合法:通过方法访问
// println(dog.age) // 错误:外部不可访问private属性
Dog.printAge(dog) // 合法:伴生对象可访问
}
}
与 Java 类的核心差异
| 特性 | Scala | Java |
|---|---|---|
| 源文件与类的关系 | 一个源文件可包含多个类(均为 public) | 一个源文件通常对应一个 public 类 |
| 属性默认访问权限 | private(通过 getter/setter 访问) | 默认是包内可见(直接访问) |
| 构造方法 | 主构造器与类体融合(后续章节详解) | 需显式定义构造方法 |
| 伴生对象 | 允许伴生对象访问类的 private 成员 | 无伴生对象概念,需用静态内部类模拟 |
| 不可变属性 | val 修饰(自动生成 final 字段) | 需显式用 final 修饰 |
最佳实践
- 优先使用 val:不可变属性(val)更符合函数式编程思想,线程安全且减少副作用。
- 合理初始化属性:避免使用
_赋默认值(除非必要),显式初始化更清晰。 - 利用访问修饰符:通过
private和protected控制属性访问,封装内部状态。 - 避免过度暴露属性:如需外部访问,优先提供方法而非直接用 var 声明(如
private var age: Int+def getAge() = age)。