简体   繁体   English

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

[英]Understanding what 'type' keyword does in Scala

I am new to Scala and I could not really find a lot about the type keyword. 我是Scala的新手,我真的找不到关于type关键字的很多东西。 I am trying to understand what the following expression may mean: 我想了解下面的表达式可能意味着什么:

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

FunctorType is some kind of an alias, but what does it signify? FunctorType是某种别名,但它表示什么?

Actually the type keyword in Scala can do much more than just aliasing a complicated type to a shorter name. 实际上,Scala中的type关键字可以做的不仅仅是将复杂类型别名化为更短的名称。 It introduces type members . 它介绍了类型成员

As you know, a class can have field members and method members. 如您所知,类可以包含字段成员和方法成员。 Well, Scala also allows a class to have type members. 好吧,Scala还允许类具有类型成员。

In your particular case type is, indeed, introducing an alias that allows you to write more concise code. 在您的特定情况下, type确实引入了一个别名,允许您编写更简洁的代码。 The type system just replaces the alias with the actual type when type-checking is performed. 在执行类型检查时,类型系统只是用实际类型替换别名。

But you can also have something like this 但你也可以有这样的东西

trait Base {
  type T

  def method: T
}

class Implementation extends Base {
  type T = Int

  def method: T = 42
}

Like any other member of a class, type members can also be abstract (you just don't specify what their value actually is) and can be overridden in implementations. 与类的任何其他成员一样,类型成员也可以是抽象的(您只是不指定它们的值实际是什么)并且可以在实现中被覆盖。

Type members can be viewed as dual of generics since much of the things you can implement with generics can be translated into abstract type members. 类型成员可以被视为泛型的双重性,因为可以使用泛型实现的大部分内容可以转换为抽象类型成员。

So yes, they can be used for aliasing, but don't limit them to just this, since they are a powerful feature of Scala's type system. 所以是的,它们可以用于别名,但不限于此,因为它们是Scala类型系统的强大功能。

Please see this excellent answer for more details: 有关详细信息,请参阅此优秀答案:

Scala: Abstract types vs generics Scala:抽象类型与泛型

Yes, the type alias FunctorType is just a shorthand for 是的, 类型别名 FunctorType只是一个简写

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

Type aliases are often used to keep the rest of the code simple: you can now write 类型别名通常用于保持代码的其余部分简单:您现在可以编写

def doSomeThing(f: FunctorType)

which will be interpreted by the compiler as 这将被编译器解释为

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

This helps to avoid defining many custom types that are just tuples or functions defined on other types, for example. 例如,这有助于避免定义许多自定义类型,这些自定义类型只是元组或在其他类型上定义的函数。

There are also several other interesting use cases for type , as described for example in this chapter of Programming in Scala . 还有其他一些type有趣用例,如Scala编程 本章中所述。

Just an example to see how to use "type" as alias : 只是一个示例,看看如何使用“类型”作为别名:

type Action = () => Unit

The definition above defines Action to be an alias of the type of procedures(methodes) that take an empty parameter list and that return Unit. 上面的定义将Action定义为采用空参数列表和返回Unit的过程类型(方法)的别名。

I liked the answer from Roland Ewald since he described with a very simple use case of type alias, and for more detail introduced a very nice tutorial. 我喜欢Roland Ewald的答案,因为他描述了一个非常简单的别名类型的用例,并且更多细节介绍了一个非常好的教程。 However, since another use case is introduced in this post named type members , I would like to mention the most practical use case of it, which I liked very much: (this part is taken from here :) 但是,由于在这篇名为类型成员的帖子中引入了另一个用例,我想提一下它最实用的用例,我非常喜欢:(这部分取自这里 :)

Abstract Type: 摘要类型:

type T

T above says that this type that is going to be used, is unknown yet, and depending on the concrete subclass, it will be defined. 上面的T表示将要使用的这种类型尚不清楚,具体取决于具体的子类,它将被定义。 The best way always for understanding the programming concepts is providing an example: Suppose you have the following scenario: 总是理解编程概念的最佳方法是提供一个示例:假设您有以下场景:

没有类型抽象

Here you will get compilation error, because eat method in classes Cow and Tiger do not override the eat method in class Animal, because their parameter types are different. 在这里你会得到编译错误,因为Cow和Tiger类中的eat方法不会覆盖Animal类中的eat方法,因为它们的参数类型不同。 It's Grass in class Cow, and Meat in class Tiger vs. Food in class Animal which is super class and all subclasses must conform. 它是Class Cow中的Grass,而类Animal中的Meat类是Animal类中的超级类,所有子类都必须符合。

Now back to type abstraction, by the following diagram and simply adding a type abstraction, you can define the type of the input, in according subclass itself. 现在回到类型抽象,通过下图并简单地添加一个类型抽象,您可以在子类本身中定义输入的类型。

用抽象类型

Now look at following codes: 现在看下面的代码:

  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

Compiler is happy and we improve our design. 编译器很高兴我们改进了我们的设计。 We can feed our cow with cow.SuitableFood and compiler prevent us with feeding out cow with the food which is suitable for Tiger. 我们可以用奶牛喂养我们的牛。适合食物和编译器阻止我们用适合老虎的食物喂养牛。 But what if we want to make difference between the type of cow1 SuitableFood and cow2 SuitabeFood. 但是,如果我们想在cow1 SuitableFood和cow2 SuitabeFood的类型之间做出改变怎么办? In another word, it would be very handy in some scenarios if the path by which we reach to the type (of course via object) does basically matter. 换句话说,如果我们达到类型的路径(当然是通过对象)确实很重要,那么在某些情况下它会非常方便。 Thanks to the advanced features in scala, it is possible: 由于scala的高级功能,它可以:

Path-dependent types: Scala objects can have types as members. 路径依赖类型: Scala对象可以将类型作为成员。 The meaning of the type, depends on the path you use to access it. 类型的含义取决于您用来访问它的路径。 The path is determined by the reference to an object (aka an instance of a class). 路径由对象(也称为类的实例)的引用确定。 In order to implement this scenario, you need to define class Grass inside the Cow, ie, Cow is the outer class and Grass is the inner class. 为了实现这个场景,你需要在Cow中定义类Grass,即,Cow是外部类,Grass是内部类。 The structure will be like this: 结构将是这样的:

  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 = {}
  }

Now if you try to compile this code: 现在,如果您尝试编译此代码:

  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

On line 4 you will see an error because Grass is now an inner class of Cow, therefore, to create an instance of Grass, we need a cow object and this cow object determines the path. 在第4行,您将看到一个错误,因为Grass现在是Cow的内部类,因此,要创建Grass的实例,我们需要一个cow对象,这个cow对象确定路径。 So 2 cow objects give rise to 2 different path. 因此,2个牛对象产生2种不同的路径。 In this scenario, cow2 only wants to eat food especially created for it. 在这种情况下,cow2只想吃特别为它创造的食物。 So: 所以:

cow2 eat new cow2.SuitableFood

Now everybody is happy :-) 现在每个人都很开心:-)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM