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 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
上面的调用编译就好了。因此,我的问题是,这种类型声明的目的究竟是什么?它们旨在解决什么问题,为什么它们不被禁止,就像对象中的其他类型的抽象成员一样?
最佳答案
对于方法(或值),只有 2 个选项:要么它有主体(然后它是“具体的”),要么没有(那么它是“抽象的”)。 A型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?
}
关于scala - 在对象中没有定义的类型声明是什么意思?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54137522/