了解Scala中'type'关键字的作用

Cor*_*ped 139 types scala

我是Scala的新手,我真的找不到关于这个type关键字的很多东西.我想了解下面的表达式可能意味着什么:

type FunctorType = (LocalDate, HolidayCalendar, Int, Boolean) => LocalDate
Run Code Online (Sandbox Code Playgroud)

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

Mar*_*ila 188

实际上type,Scala中的关键字可以做的不仅仅是将复杂类型别名化为较短的名称.它介绍了类型成员.

如您所知,类可以包含字段成员和方法成员.好吧,Scala还允许类具有类型成员.

在您的特定情况下type,确实引入了一个别名,允许您编写更简洁的代码.在执行类型检查时,类型系统只是用实际类型替换别名.

但你也可以有这样的东西

trait Base {
  type T

  def method: T
}

class Implementation extends Base {
  type T = Int

  def method: T = 42
}
Run Code Online (Sandbox Code Playgroud)

与类的任何其他成员一样,类型成员也可以是抽象的(您只是不指定它们的值实际是什么)并且可以在实现中被覆盖.

类型成员可以被视为泛型的双重性,因为可以使用泛型实现的大部分内容可以转换为抽象类型成员.

所以是的,它们可以用于别名,但不限于此,因为它们是Scala类型系统的强大功能.

有关详细信息,请参阅此优秀答案:

Scala:抽象类型与泛型

  • 重要的是要记住,在类中使用类型会创建一个类型成员而不是别名.因此,如果您只需要一个类型别名,请在随播对象中定义它. (44认同)

Rol*_*ald 139

是的,类型别名 FunctorType只是简写

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

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

def doSomeThing(f: FunctorType)
Run Code Online (Sandbox Code Playgroud)

这将被编译器解释为

def doSomeThing(f: (LocalDate, HolidayCalendar, Int, Boolean) => LocalDate)
Run Code Online (Sandbox Code Playgroud)

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

还有一些其他有趣的使用案例type,如例如描述本章编程斯卡拉.


sof*_*udi 5

只是一个示例,看看如何使用"类型"作为别名:

type Action = () => Unit
Run Code Online (Sandbox Code Playgroud)

上面的定义将Action定义为采用空参数列表和返回Unit的过程类型(方法)的别名.


Meh*_*ran 5

我喜欢Roland Ewald的回答, 因为他用一个非常简单的类型别名用例进行了描述,并且更详细地介绍了一个很好的教程。但是,由于在这篇文章中引入了另一个称为类型成员的用例,所以我想提到它的最实际的用例,这是我非常喜欢的:(这部分取自此处:)

摘要类型:

type T
Run Code Online (Sandbox Code Playgroud)

上面的T表示将要使用的这种类型尚不清楚,并且将根据具体的子类进行定义。始终了解编程概念的最佳方法是提供一个示例:假设您具有以下情形:

没有类型抽象

在这里,您会遇到编译错误,因为Cow和Tiger类中的eat方法不会覆盖Animal类中的eat方法,因为它们的参数类型不同。它是“牛”类的“草”,“老虎”类的“肉”与“动物”类的“食物”,是超类,所有子类都必须符合。

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

与抽象类型

现在看下面的代码:

  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
Run Code Online (Sandbox Code Playgroud)

编译器很高兴,我们改进了设计。我们可以用牛喂牛。SuitableFood和编译器阻止我们用适合老虎的食物喂牛。但是,如果我们想区分Cow1适合食品和Cow2吹牛食品的类型,该怎么办?换句话说,在某些情况下,如果我们到达类型(当然是通过对象)的路径确实很重要,那将非常方便。借助scala中的高级功能,可以:

路径相关类型: Scala对象可以将类型作为成员。类型的含义取决于您用来访问它的路径。路径由对对象(即类的实例)的引用确定。为了实现此方案,需要在Cow内部定义Grass类,即Cow是外部类,Grass是内部类。结构将如下所示:

  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 = {}
  }
Run Code Online (Sandbox Code Playgroud)

现在,如果您尝试编译此代码:

  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
Run Code Online (Sandbox Code Playgroud)

在第4行,您将看到一个错误,因为Grass现在是Cow的一个内部类,因此,要创建Grass的实例,我们需要一个cow对象,而该cow对象将确定路径。因此,两个牛对象会产生两个不同的路径。在这种情况下,cow2只想吃专门为其创建的食物。所以:

cow2 eat new cow2.SuitableFood
Run Code Online (Sandbox Code Playgroud)

现在每个人都很高兴:-)