You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Scala内部样例类序列化失败:ScalaTest与REPL差异排查

ScalaTest中样例类序列化抛出NotSerializableException的原因分析

问题场景

尝试对Scala中的极简样例类执行基础序列化操作,代码如下:

import org.scalatest.wordspec.AnyWordSpecLike
import java.io.{ByteArrayOutputStream, ObjectOutputStream}

class PersistenceSpec extends AnyWordSpecLike{
  case class TestClass(name: String) extends Serializable

  def serializeSomething(): ByteArrayOutputStream = {
    val testItem = TestClass("My Thing")
    val bos: ByteArrayOutputStream = new ByteArrayOutputStream()
    val oos = new ObjectOutputStream(bos)
    oos.writeObject(testItem)
    bos
  }

  "serializeSomething" when {
    "executed" must {
      "successfully serialize" in {
        val outputStream = serializeSomething()
        println(outputStream.toString())
      }
    }
  }
}

运行ScalaTest测试时,调用oos.writeObject(testItem)抛出java.io.NotSerializableException,但存在以下矛盾现象:

  • Scala样例类默认实现Serializable接口;
  • 相同代码在REPL中执行可成功序列化;
  • 将序列化对象替换为字符串"Hello"时,ScalaTest测试能正常运行。

原因解析

核心问题出在内部类的序列化规则上:

  1. 你将TestClass定义为PersistenceSpec测试类的内部类,Java/Scala中,非静态内部类会隐式持有外部类实例的引用。当序列化内部类实例时,JVM会尝试同时序列化它所引用的外部类实例。
  2. ScalaTest的测试执行环境中,PersistenceSpec的实例(即测试类本身)并未实现Serializable接口,因此序列化TestClass实例时,JVM尝试序列化外部的PersistenceSpec实例就会抛出NotSerializableException。
  3. REPL环境对内部类的处理逻辑不同:交互环境中定义的类会被当作顶级类处理,不会持有外部类引用,因此序列化时不会触发外部类的序列化检查,自然可以成功。
  4. 替换为字符串"Hello"能正常运行,是因为字符串本身是顶级类且实现了Serializable,不存在外部类引用的问题。

解决办法

方案1:将样例类移为顶级类

把TestClass移到测试类外部,作为独立的顶级类定义:

import org.scalatest.wordspec.AnyWordSpecLike
import java.io.{ByteArrayOutputStream, ObjectOutputStream}

case class TestClass(name: String)

class PersistenceSpec extends AnyWordSpecLike{
  def serializeSomething(): ByteArrayOutputStream = {
    val testItem = TestClass("My Thing")
    val bos: ByteArrayOutputStream = new ByteArrayOutputStream()
    val oos = new ObjectOutputStream(bos)
    oos.writeObject(testItem)
    bos
  }

  "serializeSomething" when {
    "executed" must {
      "successfully serialize" in {
        val outputStream = serializeSomething()
        println(outputStream.toString())
      }
    }
  }
}

方案2:将样例类放在嵌套对象中

在测试类内部定义一个嵌套对象,把TestClass放在该对象中,这样它就不会持有外部测试类的引用:

import org.scalatest.wordspec.AnyWordSpecLike
import java.io.{ByteArrayOutputStream, ObjectOutputStream}

class PersistenceSpec extends AnyWordSpecLike{
  object TestModels {
    case class TestClass(name: String)
  }

  def serializeSomething(): ByteArrayOutputStream = {
    val testItem = TestModels.TestClass("My Thing")
    val bos: ByteArrayOutputStream = new ByteArrayOutputStream()
    val oos = new ObjectOutputStream(bos)
    oos.writeObject(testItem)
    bos
  }

  "serializeSomething" when {
    "executed" must {
      "successfully serialize" in {
        val outputStream = serializeSomething()
        println(outputStream.toString())
      }
    }
  }
}

内容的提问来源于stack exchange,提问作者JoeMjr2

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 15:30:54