scala关键字val、var、def的区别

本文通过实例详细解析了Scala中val、var与def的区别,并解释了为何实现类可以在没有明确实现接口方法的情况下直接使用该方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近做项目时碰到scala一个很难理解的问题,一个接口里定义了某个方法,实现类里什么实现代码都没有,就是声明了一个同名的变量就可以直接运行了,真是莫名其妙(说实话不喜欢Scala,太多例外了)。案例代码如下:

接口A:

trait A {
   def dataMap: Map[String, String]
}

实现类B:

class B extends A {
   val dataMap = Map[String,String]()
}
然后在B类中就可以直接使用接口A的dataMap方法了。明明都没有实现dataMap方法,怎么就可以直接使用这个方法了?


在回答上面的问题之前,先看下面的例子:

/**
  * Description: To Test the keyword of val,var,def.
  *
  * Author : Adore Chen
  * Created: 2018-01-23
  */
class Test {

  val f1: Int = { println("val"); 1 }

  var f2: Int = { println("var"); 2}

  def f3: Int = { println("def"); 3}

  def main(args: Array[String]): Unit = {

    val f4: Int = 1

    var f5: Int = 2

    println(f4)

  }

}
上面的测试类,使用val,var,def 并对他们不同位置进行了测试。我们使用 scalac Test.scala编译成Test.class,然后使用javap反编译查看具体实现:

javap -p -c Test
反编译的代码如下:

Compiled from "Test.scala"
public class Test {
  private final int f1;

  private int f2;

  public int f1();
    Code:
       0: aload_0
       1: getfield      #14                 // Field f1:I
       4: ireturn

  public int f2();
    Code:
       0: aload_0
       1: getfield      #18                 // Field f2:I
       4: ireturn

  public void f2_$eq(int);
    Code:
       0: aload_0
       1: iload_1
       2: putfield      #18                 // Field f2:I
       5: return

  public int f3();
    Code:
       0: getstatic     #28                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
       3: ldc           #30                 // String def
       5: invokevirtual #34                 // Method scala/Predef$.println:(Ljava/lang/Object;)V
       8: iconst_3
       9: ireturn

  public void main(java.lang.String[]);
    Code:
       0: iconst_1
       1: istore_2
       2: iconst_2
       3: istore_3
       4: getstatic     #28                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
       7: iload_2
       8: invokestatic  #43                 // Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;
      11: invokevirtual #34                 // Method scala/Predef$.println:(Ljava/lang/Object;)V
      14: return

  public Test();
    Code:
       0: aload_0
       1: invokespecial #50                 // Method java/lang/Object."<init>":()V
       4: aload_0
       5: getstatic     #28                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
       8: ldc           #52                 // String val
      10: invokevirtual #34                 // Method scala/Predef$.println:(Ljava/lang/Object;)V
      13: iconst_1
      14: putfield      #14                 // Field f1:I
      17: aload_0
      18: getstatic     #28                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
      21: ldc           #54                 // String var
      23: invokevirtual #34                 // Method scala/Predef$.println:(Ljava/lang/Object;)V
      26: iconst_2
      27: putfield      #18                 // Field f2:I
      30: return
}
从上面反编译的代码可以看出:

1)val是java的final不可变变量,var是java的普通变量;

2)在main函数里,val和var仅声明变量;

3)在class类定义里,val和var是先声明field存储空间,然后分别为他们定了同名的方法。

val 变量定义了 同名方法(类似于getter),来获取它的变量值;

public int f1();

var 变量定义了 同名方法(类似getter) 来获取它的变量值,同时提供了修改变量值的方法(类似于setter)

public int f2();
public void f2_$eq(int);

4)val/var和def的区别,可以从callByValue 和 callByName的角度来理解,val,var是callByValue, def是callByName.

callByValue 和callByName的区别,请移步:http://blog.youkuaiyun.com/qq_29343201/article/details/56281777






<think>我们讨论的是Scala中使用def定义方法并返回new实例的用法场景。根据用户之前的引用问题,我们可以从以下几个方面展开:1.**基本用法**:def方法如何返回一个new创建的实例2.**应用场景**:在什么情况下我们会这样做3.**注意事项**:比如线程安全、性能、设计模式等4.**相关模式**:工厂模式、伴生对象apply方法等根据引用[1],我们知道Scala中有一个特殊的apply方法,当我们在对象后使用括号时,Scala会自动调用apply方法。但是这里用户明确问的是使用def定义方法返回new实例,所以我们要区分普通的def方法apply方法的异同。根据引用[2],我们知道unapply方法通常用于模式匹配,而apply方法通常用于构建对象。所以当我们使用def返回new实例时,通常可以用于替代apply方法或者作为工厂方法。根据引用[3],trait可以用于定义抽象接口,我们可以通过def方法返回一个trait的具体实现实例。这在构建对象时很常见。以下我们将详细说明:##1.基本用法在Scala中,我们可以使用`def`定义一个方法,该方法使用`new`关键字创建类的实例并返回。例如:```scalaclassPerson(name:String,age:Int)//定义一个方法返回新的Person实例defcreatePerson(name:String,age:Int):Person=newPerson(name,age)//使用valperson=createPerson("Alice",30)```##2.应用场景###(1)工厂方法当创建对象需要一些额外的逻辑(例如参数验证、日志记录等)时,我们可以使用工厂方法封装创建过程。```scalaclassPersonprivate(name:String,age:Int)//主构造器私有化objectPerson{//工厂方法defcreate(name:String,age:Int):Person={require(age>=0,"Agemustbenon-negative")newPerson(name,age)}}```###(2)替代apply方法在伴生对象中,我们通常使用apply方法作为构造器的替代。但如果我们需要更明确的名称(例如工厂方法需要描述性名称),则可以使用def定义方法。例如:```scalaclassPersonprivate(valname:String,valage:Int)objectPerson{//使用apply方法defapply(name:String,age:Int):Person=newPerson(name,age)//或者使用def定义具有明确名称的方法defcreateAdult(name:String,minAge:Int=18):Person=newPerson(name,minAge)}```###(3)返回不同类型的实例当我们需要根据条件返回不同子类的实例时,就可以使用def方法。这结合多态使用非常有效。例如:```scalatraitAnimalclassDogextendsAnimalclassCatextendsAnimaldefcreateAnimal(kind:String):Animal=kindmatch{case"dog"=>newDogcase"cat"=>newCat}```###(4)在trait中定义创建方法引用[3]展示了trait的使用。我们可以定义一个trait,其中包含一个返回实例的方法。例如:```scalatraitAnimalFactory{defcreate():Animal}classDogFactoryextendsAnimalFactory{defcreate():Animal=newDog}```##3.注意事项###(1)线程安全如果多个线程同时调用创建方法,需要注意所创建对象的线程安全以及工厂方法本身的线程安全。通常,如果只是创建新实例而不共享状态,是线程安全的。###(2)性能频繁创建对象可能会带来性能开销,特别是当对象创建过程复杂时。可以考虑使用对象池等技术优化,但不要过早优化。###(3)依赖注入当创建的对象需要依赖其他对象时,我们可以通过方法参数传递依赖,也可以使用隐式参数(Scala特有方式)。```scalaclassService(dependency:Dependency)defcreateService(implicitdep:Dependency):Service=newService(dep)```###(4)与new关键字的对比在Scala中,我们也可以直接使用`new`创建对象。但使用def方法封装创建过程可以提供更好的抽象灵活性。##4.与apply方法的比较在伴生对象中,使用`apply`方法可以让我们通过类名直接创建对象而不需要`new`,例如`Person("Alice",30)`。但有时我们希望方法名有更明确的语义(如`create`),则可以使用def定义。但通常,Scala程序员更习惯使用apply作为工厂方法。##示例:综合应用以下是一个综合示例,结合trait、多态工厂方法:```scala//引用[3]的trait应用traitAnimal{defspeak:Unit}classDogextendsAnimal{defspeak:Unit=println("Woof!")}classCatextendsAnimal{defspeak:Unit=println("Meow!")}//工厂traittraitAnimalFactory{defcreateAnimal:Animal}objectDogFactoryextendsAnimalFactory{defcreateAnimal:Animal=newDog}objectCatFactoryextendsAnimalFactory{defcreateAnimal:Animal=newCat}//通过def方法返回新实例defadoptAnimal(factory:AnimalFactory):Animal=factory.createAnimal//使用valdog=adoptAnimal(DogFactory)```综上所述,在Scala中使用`def`返回`new`实例是一种常用的工厂模式,提供了灵活的对象创建方式,特别是在需要封装创建逻辑、支持多态或者需要描述性方法名的情况下。§§相关问题§§1.Scala中工厂方法apply方法有什么异同点?2.在Scala中如何实现线程安全的对象工厂?3.使用def返回new实例直接使用new创建对象在性能上有何差异?4.如何利用Scala的trait实现抽象工厂模式?</think> ### Scala中使用def定义方法返回new实例的用法场景分析 在Scala中,使用`def`定义方法并返回`new`创建的新实例是一种常见模式,主要用于以下场景: #### 🛠️ 核心用法 ```scala class MyClass(param: String) // 定义返回新实例的方法 def createInstance(param: String): MyClass = { new MyClass(param) // 返回new创建的新对象 } // 使用 val obj = createInstance("test") ``` - **特征**: - 💡 每次调用都会生成**全新的对象实例** - ✅ 可封装复杂初始化逻辑(如参数校验、依赖注入等) - 🔄 适用于需要控制对象创建过程的场景 #### 🔍 典型应用场景 1. **工厂模式实现** 封装对象创建细节,返回接口实现: ```scala trait Player { def play(): Unit } def createPlayer(playerType: String): Player = playerType match { case "audio" => new AudioPlayer() // 返回具体实现 case "video" => new VideoPlayer() } ``` *结合引用[3]中特质使用场景[^3]* 2. **构建不可变对象** ```scala case class Config(server: String, port: Int) def loadConfig(): Config = { // 从文件/DB加载配置 new Config("localhost", 8080) // 返回新实例 } ``` 3. **带缓存的实例创建** 在特定条件下复用实例: ```scala private var cachedInstance: Option[MyClass] = None def getSingletonInstance(param: String): MyClass = { if (cachedInstance.isEmpty) { cachedInstance = Some(new MyClass(param)) } cachedInstance.get } ``` 4. **带预处理的对象创建** 在构造前执行校验逻辑: ```scala class ValidatedInput(value: Int) def createInput(raw: String): ValidatedInput = { require(raw.forall(_.isDigit), "必须为数字") new ValidatedInput(raw.toInt) } ``` #### ⚠️ 重要注意事项 1. **线程安全问题** 若方法包含可变状态(如计数器),需使用`synchronized`: ```scala private var idCounter = 0 def newItem(): Item = synchronized { idCounter += 1 new Item(idCounter) // 线程安全的ID分配 } ``` 2. **与apply方法的区别** | 特性 | `def`方法返回`new` | 伴生对象`apply` | |--------------|--------------------------|--------------------------| | 调用语法 | `createInstance()` | `ClassName()` | | 自动触发机制 | 无 | Scala编译器自动调用[^1] | | 典型用途 | 复杂对象构建 | 简化构造函数调用 | ```scala // apply方法示例(引用[1]) object MyClass { def apply(param: String) = new MyClass(param) } ``` 3. **性能考量** - 💥 频繁创建大对象可能导致GC压力 - ✅ 对小对象合理使用场景效率良好 - ⚙️ 必要时使用对象池优化 #### 💡 最佳实践 1. **配合特质使用** 返回特质类型增强灵活性(引用[3]): ```scala trait Player { def play(): Unit } class AudioPlayer extends Player { ... } def createPlayer(): Player = new AudioPlayer() ``` 2. **类型参数化** 创建泛型工厂方法: ```scala def createContainer[T](elem: T): Box[T] = new Box(elem) ``` 3. **DSL构建** 实现流畅接口: ```scala def query(table: String): QueryBuilder = new QueryBuilder(table) // 使用链式调用 query("users").where("age > 18").limit(10) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值