Scala允许使用type
关键字定义类型,这些类型通常具有不同的含义和目的,具体取决于声明的时间。
如果在对象或包对象中使用type
,则需要定义类型别名,即另一种类型的简称/简称:
package object whatever {
type IntPredicate = Int => Boolean
def checkZero(p: IntPredicate): Boolean = p(0)
}
在类/特征中声明的类型通常旨在在子类/子特征中被覆盖,并最终也解析为具体类型:
trait FixtureSpec {
type FixtureType
def initFixture(f: FixtureType) = ...
}
trait SomeSpec extends FixtureSpec {
override type FixtureType = String
def test(): Unit = {
initFixture("hello")
...
}
}
抽象类型声明还有其他用途,但无论如何最终它们都会解析为某些具体类型。
但是,还有一个选项可以在 object 内声明抽象类型(即没有实际定义):
object Example {
type X
}
这会编译,而不是像抽象方法:
object Example {
def method: String // compilation error
}
由于无法扩展对象,因此无法将其解析为具体类型。
我假设这样的类型定义可以方便地用作幻像类型。例如(使用Shapeless的标记类型):
import shapeless.tag.@@
import shapeless.tag
type ++>[-F, +T]
trait Converter
val intStringConverter: Converter @@ (String ++> Int) = tag[String ++> Int](...)
但是,似乎类型系统对待这些类型的方式与常规类型不同,这导致上述“抽象”类型的使用在某些情况下失败。
尤其是,当寻找隐式参数时,Scala最终将查找与“关联”类型相关联的隐式范围,即,隐式参数的类型签名中存在的类型。但是,当使用“抽象”类型时,这些关联类型的嵌套似乎存在一些限制。考虑以下示例设置:
import shapeless.tag.@@
trait Converter
type ++>[-F, +T]
case class DomainType()
object DomainType {
implicit val converter0: Converter @@ DomainType = null
implicit val converter1: Converter @@ Seq[DomainType] = null
implicit val converter2: Converter @@ (Seq[String] ++> Seq[DomainType]) = null
}
// compiles
implicitly[Converter @@ DomainType]
// compiles
implicitly[Converter @@ Seq[DomainType]]
// fails!
implicitly[Converter @@ (Seq[String] ++> Seq[DomainType])]
在这里,前两个隐式解决方案可以很好地编译,而最后一个隐式解决方案失败,并出现有关缺少的隐式解决方案的错误。如果我在与implicitly
调用相同的作用域中定义隐式函数,则它将编译:
implicit val converter2: Converter @@ (Seq[String] ++> Seq[DomainType]) = null
// compiles
implicitly[Converter @@ (Seq[String] ++> Seq[DomainType])]
但是,如果我将++>
的定义改为trait
而不是type
:
trait ++>[-F, +T]
然后上面的所有implicitly
调用都可以编译。
因此,我的问题是,此类类型声明的确切目的是什么?它们打算解决什么问题?为什么不像对象中的其他抽象成员一样禁止使用它们?
答案 0 :(得分:4)
对于一个方法(或值),只有2个选项:它具有主体(然后为“具体”)或没有主体(则为“抽象”)。类型X
始终是某个类型间隔X >: LowerBound <: UpperBound
(如果将LowerBound = UpperBound
称为具体间隔,或者将LowerBound = Nothing
,UpperBound = Any
称为完全抽象,但情况多种多样之间)。因此,如果我们要禁止对象中使用抽象类型,则应该始终有办法检查LowerBound
和UpperBound
类型是否相等。但是可以用一些复杂的方式定义它们,通常这种检查并不容易:
object Example {
type X >: N#Add[N] <: N#Mult[Two] // Do we expect that compiler proves n+n=n*2?
}