了解“ type”关键字在 Scala 中的作用

我是 Scala 的新手,我找不到太多关于 type关键字的信息。我试图理解下面的表达可能是什么意思:

type FunctorType = (LocalDate, HolidayCalendar, Int, Boolean) => LocalDate

FunctorType是某种化名,但它意味着什么呢?

76899 次浏览

是的,类型化名 FunctorType只是

(LocalDate, HolidayCalendar, Int, Boolean) => LocalDate

类型别名通常用于保持代码的其余部分简单: 您现在可以编写

def doSomeThing(f: FunctorType)

将被编译器解释为

def doSomeThing(f: (LocalDate, HolidayCalendar, Int, Boolean) => LocalDate)

这有助于避免定义许多自定义类型,这些类型只是在其他类型上定义的元组或函数,例如。

type还有其他一些有趣的用例,如 Scala 编程这一章中所描述的。

实际上,Scala 中的 type关键字可以做的不仅仅是将一个复杂的类型别名为一个更短的名称。它引入了 类型成员

如您所知,类可以有字段成员和方法成员。Scala 还允许类有类型成员。

在您的特殊情况下,type实际上引入了一个别名,使您能够编写更简洁的代码。在执行类型检查时,类型系统只用实际类型替换别名。

但你也可以拥有这样的东西

trait Base {
type T


def method: T
}


class Implementation extends Base {
type T = Int


def method: T = 42
}

与类的任何其他成员一样,类型成员也可以是抽象的(只是不指定它们的实际值) ,并且可以在实现中重写。

可以将类型成员视为泛型的对偶,因为可以用泛型实现的许多事情都可以转换为抽象类型成员。

因此,是的,它们可以用于别名,但是不要仅限于此,因为它们是 Scala 类型系统的一个强大特性。

更多细节请参考这个精彩的答案:

Scala: 抽象类型 vs 泛型

下面是一个如何使用“ type”作为别名的示例:

type Action = () => Unit

上面的定义将 Action 定义为接受空参数列表并返回 Unit 的过程(方法)类型的别名。

我喜欢来自 Roland Ewald的答案,因为他用一个非常简单的别名类型用例进行了描述,并且为更多细节介绍了一个非常好的教程。 然而,由于在这篇文章中引入了另一个用例,名为 类型成员,我想提到它最实际的用例,我非常喜欢: (这部分取自 给你:)

摘要类型:

type T

上面的 T 说,这个类型将要被使用,是未知的,并且根据具体的子类,它将被定义。 理解编程概念的最好方法总是提供一个示例: 假设您有以下场景:

Without Type Abstraction

这里会得到编译错误,因为类 Cow 和 Tiger 中的 eat 方法没有覆盖类 Animal 中的 eat 方法,因为它们的参数类型不同。牛是草,虎是肉,食物是食物,动物是超级类,所有的子类都必须遵守。

现在回到类型抽象,通过下面的图和简单地添加一个类型抽象,您可以根据子类本身定义输入的类型。

With Abstract Type

现在看看下面的代码:

  val cow1: Cow = new Cow
val cow2: Cow = new Cow


cow1 eat new cow1.SuitableFood
cow2 eat new cow1.SuitableFood


val tiger: Tiger = new Tiger
cow1 eat new tiger.SuitableFood // Compiler error

编译器是快乐的,我们改进我们的设计。我们可以用奶牛喂我们的奶牛。合适的食物和编译器防止我们用适合老虎的食物喂牛。但是如果我们想要区分一下“适合的食物”和“适合的食物”的类型。换句话说,如果我们到达类型的路径(当然是通过对象)基本上很重要,那么在某些场景中会非常方便。由于 Scala 的先进特性,这种方法是可行的:

路径依赖类型: Scala 对象可以将类型作为成员。类型的含义取决于您用来访问它的路径。路径由对对象(即类的实例)的引用决定。 为了实现这个场景,您需要在 Cow 内部定义 class Gross,也就是说,Cow 是外部类,而 Gross 是内部类。结构是这样的:

  class Cow extends Animal {
class Grass extends Food
type SuitableFood = Grass
override def eat(food: this.SuitableFood): Unit = {}
}


class Tiger extends Animal {
class Meat extends Food
type SuitableFood = Meat
override def eat(food: this.SuitableFood): Unit = {}
}

现在,如果你尝试编译这段代码:

  1. val cow1: Cow = new Cow
2. val cow2: Cow = new Cow


3. cow1 eat new cow1.SuitableFood
4. cow2 eat new cow1.SuitableFood // compilation error

在第4行你会看到一个错误,因为 Gross 现在是 Cow 的一个内部类,因此,要创建一个 Gross 的实例,我们需要一个 ow 对象,而这个 bow 对象决定了路径。所以两个奶牛物体产生了两条不同的路径。 在这种情况下,牛2只想吃专门为它制作的食物,所以:

cow2 eat new cow2.SuitableFood

现在大家都高兴了: -)