【问题标题】:Understanding what 'type' keyword does in Scala理解 Scala 中的“类型”关键字的作用
【发布时间】:2013-10-29 19:52:52
【问题描述】:

我是 Scala 的新手,我真的找不到很多关于 type 关键字的信息。我试图理解以下表达式可能意味着什么:

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

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

【问题讨论】:

    标签: scala types


    【解决方案1】:

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

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

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

    但你也可以有这样的东西

    trait Base {
      type T
    
      def method: T
    }
    
    class Implementation extends Base {
      type T = Int
    
      def method: T = 42
    }
    

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

    类型成员可以看作是泛型的双重成员,因为可以用泛型实现的大部分东西都可以转换为抽象类型成员。

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

    请参阅这个出色的答案了解更多详情:

    Scala: Abstract types vs generics

    【讨论】:

    • 重要的是要记住,在类中使用类型会创建类型成员而不是别名。因此,如果您只需要一个类型别名,请在伴随对象中定义它。
    【解决方案2】:

    是的,类型别名 FunctorType 只是

    的简写

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

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

    def doSomeThing(f: FunctorType)
    

    编译器会将其解释为

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

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

    type 还有几个其他有趣的用例,例如 Scala 编程this chapter 中所述。

    【讨论】:

      【解决方案3】:

      我喜欢Roland Ewald 的回答,因为他描述了一个非常简单的别名类型用例,并且更详细地介绍了一个非常好的教程。 但是,由于这篇文章中介绍了另一个名为 type members 的用例,所以我想提一下它最实际的用例,我非常喜欢: (此部分摘自here:)

      抽象类型:

      type T
      

      上面的 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
      

      编译器很高兴,我们改进了我们的设计。我们可以用牛喂牛。SuitableFood 和编译器阻止我们用适合老虎的食物喂牛。但是,如果我们想区分cow1 适用食品和cow2 SuitabeFood 的类型怎么办。换句话说,如果我们到达类型的路径(当然是通过对象)基本上很重要,那么在某些情况下会非常方便。由于 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 = {}
        }
      

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

        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 行,您将看到一个错误,因为 Grass 现在是 Cow 的内部类,因此,要创建 Grass 的实例,我们需要一个奶牛对象,而这个奶牛对象决定了路径。所以 2 个奶牛对象会产生 2 条不同的路径。 在这种情况下,cow2 只想吃专门为它制作的食物。所以:

      cow2 eat new cow2.SuitableFood
      

      现在大家都很开心:-)

      【讨论】:

        【解决方案4】:

        只是一个例子,看看如何使用“type”作为别名:

        type Action = () => Unit
        

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

        【讨论】:

          猜你喜欢
          • 2016-03-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-11-12
          • 1970-01-01
          • 2014-03-16
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多