0Pricing
Scala for Backend Engineering & Functional Programming · 课时

智能构造器

经过验证的构造

智能构造器 是 CoddyKit 上的免费 Scala for Backend Engineering & Functional Programming 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Scala for Backend Engineering & Functional Programming 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Scala for Backend Engineering & Functional Programming 课程共包含 4 节课。

本课时的部分内容尚未翻译,以英文显示。

Validated Construction

A smart constructor is a factory that validates inputs before producing a value. It guarantees that any instance you hold satisfies the type's invariants.

  • The raw constructor is hidden.
  • Only validated creation is public.
class Age private (val value: Int)
object Age:
  def of(v: Int): Option[Age] =
    if v >= 0 then Some(new Age(v)) else None

object Main:
  def main(args: Array[String]): Unit =
    println(Age.of(30).map(_.value))

Private Constructor

Marking the primary constructor private stops callers from bypassing validation. The companion object becomes the single entry point.

class Percentage private (val value: Int)
object Percentage:
  def of(v: Int): Option[Percentage] =
    if v >= 0 && v <= 100 then Some(new Percentage(v)) else None

object Main:
  def main(args: Array[String]): Unit =
    println(Percentage.of(50).map(_.value))
    println(Percentage.of(150))

Returning Option

Returning Option signals that construction may fail. The caller must handle None, so invalid data never silently flows through.

class NonEmpty private (val value: String)
object NonEmpty:
  def of(s: String): Option[NonEmpty] =
    if s.nonEmpty then Some(new NonEmpty(s)) else None

object Main:
  def main(args: Array[String]): Unit =
    println(NonEmpty.of("").map(_.value))
    println(NonEmpty.of("hi").map(_.value))

Returning Either for Error Detail

When you want to explain why validation failed, return Either[Error, T]. The Left carries a descriptive message.

class Username private (val value: String)
object Username:
  def of(s: String): Either[String, Username] =
    if s.isEmpty then Left("empty")
    else if s.length > 10 then Left("too long")
    else Right(new Username(s))

object Main:
  def main(args: Array[String]): Unit =
    println(Username.of(""))
    println(Username.of("ada").map(_.value))

Smart Constructors with case class

A case class with a private constructor still works. Note its generated apply and copy must also be controlled, so define a custom factory and keep the constructor private.

case class Email private (value: String)
object Email:
  def of(s: String): Option[Email] =
    if s.contains("@") then Some(Email(s)) else None

object Main:
  def main(args: Array[String]): Unit =
    println(Email.of("a@b.com"))
    println(Email.of("bad"))

Chaining Validated Values

Because smart constructors return Option or Either, you can compose them with for comprehensions to build larger validated objects.

case class Name private (value: String)
object Name:
  def of(s: String): Option[Name] =
    if s.nonEmpty then Some(Name(s)) else None

case class Person(name: Name, age: Int)

object Main:
  def make(n: String): Option[Person] =
    for nm <- Name.of(n) yield Person(nm, 20)

  def main(args: Array[String]): Unit =
    println(Main.make("Bob"))

Normalizing Input

A smart constructor can also normalize data, for example trimming whitespace or lowercasing, so all instances share a canonical form.

case class Tag private (value: String)
object Tag:
  def of(s: String): Option[Tag] =
    val clean = s.trim.toLowerCase
    if clean.nonEmpty then Some(Tag(clean)) else None

object Main:
  def main(args: Array[String]): Unit =
    println(Tag.of("  Scala  "))

Invariants Hold Forever

Once a value passes the smart constructor, its invariant is guaranteed for its whole lifetime. Downstream code can trust the value without re-checking.

case class PositiveInt private (value: Int)
object PositiveInt:
  def of(n: Int): Option[PositiveInt] =
    if n > 0 then Some(PositiveInt(n)) else None

object Main:
  def doubleIt(p: PositiveInt): Int = p.value * 2 // always positive

  def main(args: Array[String]): Unit =
    PositiveInt.of(5).foreach(p => println(Main.doubleIt(p)))

Combining with Opaque Types

For zero-cost validated values, pair a smart constructor with an opaque type. No wrapper object is allocated, yet validation still gates construction.

object Domain:
  opaque type Score = Int
  def of(n: Int): Option[Score] =
    if n >= 0 && n <= 100 then Some(n) else None
  extension (s: Score) def value: Int = s

object Main:
  def main(args: Array[String]): Unit =
    println(Domain.of(88).map(_.value))

Multiple Validation Rules

Apply several checks in sequence. The first failing rule short-circuits, returning a clear error.

object Password:
  def of(s: String): Either[String, String] =
    if s.length < 8 then Left("too short")
    else if !s.exists(_.isDigit) then Left("need a digit")
    else Right(s)

object Main:
  def main(args: Array[String]): Unit =
    println(Password.of("abc"))
    println(Password.of("abcdef12"))

When to Use Smart Constructors

Use them whenever a type has invariants that raw construction could violate.

  • Bounded numbers, non-empty strings, formatted ids.
  • Return Option for simple yes/no, Either for reasons.
  • Keep the raw constructor private.
case class Port private (value: Int)
object Port:
  def of(n: Int): Option[Port] =
    if n >= 1 && n <= 65535 then Some(Port(n)) else None

object Main:
  def main(args: Array[String]): Unit =
    println(Port.of(8080))
    println(Port.of(70000))

Quick Check

Test your understanding of smart constructors.

Recap

You learned smart constructors.

  • Validate inputs in a companion factory.
  • Make the raw constructor private.
  • Return Option or Either to force handling of failure.
  • Optionally normalize input to a canonical form.
  • Invariants then hold for the value's whole lifetime.
case class Even private (value: Int)
object Even:
  def of(n: Int): Option[Even] =
    if n % 2 == 0 then Some(Even(n)) else None

object Main:
  def main(args: Array[String]): Unit =
    println(Even.of(4))
    println(Even.of(5))

常见问题解答

「智能构造器」课时是免费的吗?

是的 — 「智能构造器」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Scala for Backend Engineering & Functional Programming 课程的其余内容,请升级到 CoddyKit PRO。 Scala for Backend Engineering & Functional Programming 课程共包含 4 节课。

「智能构造器」这节课中我会学到什么?

经过验证的构造 你通过在浏览器中直接运行的动手代码来练习 Scala for Backend Engineering & Functional Programming,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Scala for Backend Engineering & Functional Programming 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Scala for Backend Engineering & Functional Programming 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。

「智能构造器」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Scala for Backend Engineering & Functional Programming 课中编写并运行代码吗?

能。每节 Scala for Backend Engineering & Functional Programming 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 使用代数数据类型建模
  2. 智能构造器
  3. 新类型
  4. 组合领域模型
← 返回 Scala for Backend Engineering & Functional Programming