Scala:有没有一种方法可以将类型别名与别名区别开来?

时间:2018-10-03 17:54:34

标签: scala casting implicit-conversion typechecking

给出以下示例:我想截断一个字符串以满足某些长度限制,例如与SQL类型的兼容性。

type varchar8 = String

implicit def str2Varchar8(str: String): varchar8 = str.take(8)

val a: varchar8 = "abcdefghi"

// wanted: "abcdefgh", actual result:
a: varchar8 = abcdefghi

似乎编译器无法区分这两种类型。

给出类型别名type A = String,我想要实现的是:

  1. 避免运行时分配(即包装器类)
  2. 仅当从String映射到类型别名A时才应用断言/转换的能力。即直接使用别名A作为输入时避免进一步的断言/转换

验证示例:

type NotNullA = A

def method(a: A) = if(a != null)
    _method(a: NotNullA) // explicit typing
  else
    ???

// "a" at runtime is a String but we consider it validated, instead relying on the type system
protected def _method(a: NotNullA) = ???
protected def _otherMethod(a: NotNullA) = ???

有没有一种方法可以将类型别名与它们的别名类型分开,从而使隐式转换和它们之间的类型检查成为可能?还有其他可以完成此工作的编码/技术吗?

侧面:我似乎想起了一个点,即两个分开且类型和别名是不同的(与类型无关性问题无关)。我以前的代码是这样的:

type FieldAType = Int

// and in a different class
def method(a: FieldAType) = ???

val b: FieldAType = 1
method(b) // worked

val c: Int = 1
method(c) // compiler error
method(c: FieldAType) // worked

但是,我无法重现此问题(可能是由于Scala版本较旧-当前使用2.11.8)

3 个答案:

答案 0 :(得分:0)

据我所知,这是不可能的。别名只是这个别名。纯粹出于可读性。

但是,您可以使用value classes进行此操作。它们是正确的不同类型,因此您可以在代码中以不同的方式处理它们。但是大多数时候,编译器能够避免实际分配包装对象-链接的页面上有关于此异常的更多信息。

答案 1 :(得分:0)

我建议您看看softwaremill.scala-common.tagging库。

  • 没有运行时开销

  • 保护类型的可靠方法

只需添加导入并定义您的标记类型:

import com.softwaremill.tagging._

type EvenTag
type EvenInt = Int @@ EvenTag

object EvenInt {
  def fromInt(i: Int): Option[EvenInt] =
    if (i % 2 == 0) Some(i.taggedWith[EvenTag]) else None
}

def printEvenInt(evenInt: EvenInt): Unit = println(evenInt)

EvenInt.fromInt(2).foreach(printEvenInt)

val evenInt: EvenInt = 2 // Doesn't compile
printEvenInt(2) // Doesn't compile

我们如何破解它?

val evenInt: EvenInt = 1.taggedWith[EvenTag]

享受!

答案 2 :(得分:0)

Scala 3:opaque type s中可能会实现一项功能。

它们从字面上解决了您所描述的问题:能够根据名称而不是实际的基础类型来区分普通类型的别名。

看看the official proposal