Scalaで「type」キーワードが何をするかを理解する


144

私はScalaを初めて使用するので、typeキーワードについて多くを見つけることができませんでした。次の表現の意味を理解しようとしています。

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

FunctorType ある種のエイリアスですが、それは何を意味していますか?

回答:


148

はい、型のエイリアス FunctorTypeは単なる省略形です

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

タイプエイリアスは、コードの残りの部分を単純にするためによく使用されます。

def doSomeThing(f: FunctorType)

これはコンパイラによって次のように解釈されます

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

これにより、たとえば、タプルや他のタイプで定義された関数などの多くのカスタムタイプを定義する必要がなくなります。

またtype、たとえばScalaでのプログラミングのこの章で説明されているように、他にも興味深い使用例がいくつかあります。


198

実際、typeScala のキーワードは、複雑な型を短い名前にエイリアスするだけではありません。タイプメンバーを紹介します。

ご存知のように、クラスはフィールドメンバーとメソッドメンバーを持つことができます。まあ、Scalaではクラスに型メンバーを含めることもできます。

あなたの特定のケースでtypeは、確かに、より簡潔なコードを書くことができるエイリアスを導入しています。型システムは、型チェックが実行されるときに、エイリアスを実際の型に置き換えるだけです。

しかし、あなたはこのようなものを持つこともできます

trait Base {
  type T

  def method: T
}

class Implementation extends Base {
  type T = Int

  def method: T = 42
}

クラスの他のメンバーと同様に、型メンバーも抽象(実際の値を指定しない)にすることができ、実装でオーバーライドすることができます。

ジェネリックで実装できるものの多くは抽象型メンバーに変換できるため、タイプメンバーはジェネリックのデュアルとして表示できます。

そうです、そうです、それらはエイリアシングに使用できますが、Scalaの型システムの強力な機能であるため、これだけに限定しないでください。

詳細については、この優れた回答をご覧ください。

Scala:抽象型とジェネリック


44
クラス内でタイプを使用すると、エイリアスの代わりにタイプメンバーが作成されることに注意してください。したがって、タイプエイリアスだけが必要な場合は、コンパニオンオブジェクトで定義します。
リュディガーKlaehn

9

Roland Ewaldの回答は、タイプエイリアスの非常に単純なユースケースで説明しており、詳細については非常に優れたチュートリアルを紹介していたので、気に入っています 。別のユースケースは、名前のこの記事で紹介しているので、タイプのメンバー、私はとても気に入って、それの最も実用的なユースケース、言及したいと思います。(この部分はから取られ、ここに :)

抽象型:

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とコンパイラーは、タイガーに適した食品を牛に与えることを防ぎます。しかし、牛1適した食品と牛2吹田食品のタイプを区別したい場合はどうでしょう。言い換えると、型に(もちろんオブジェクトを介して)到達するパスが基本的に重要である場合、状況によっては非常に便利です。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のインスタンスを作成するには、Cowオブジェクトが必要で、このCowオブジェクトがパスを決定します。したがって、2つの牛オブジェクトは2つの異なるパスを生成します。このシナリオでは、cow2は特別に作成された食品のみを食べたいと考えています。そう:

cow2 eat new cow2.SuitableFood

今、みんなが幸せです:-)


5

"type"をエイリアスとして使用する方法を示すための単なる例:

type Action = () => Unit

上記の定義では、Actionを、空のパラメーターリストを受け取り、Unitを返すプロシージャ(メソッド)のタイプのエイリアスとして定義しています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.