web-dev-qa-db-ja.com

Scala(およびJava)のクラスと型の違いは何ですか?

Scala

Scalaのクラスとタイプの違いはどこで観察できますか?また、この区別が重要なのはなぜですか?

それは言語設計の観点からの考慮事項にすぎないのでしょうか、それともScalaをプログラミングするときに「実用的な」影響を与えるのでしょうか。

それとも、型システムの「境界を確保する」ことが基本ですか(NothingNullが頭に浮かぶ)?

Java

上記の考慮事項/相違点/問題のうち、Javaでも認識できるものはいくつありますか?


(言語に依存しない紹介として タイプとクラスの違いは何ですか? を参照してください。)

54
soc

あなたが「タイプ」と言うとき、私はあなたが主に静的タイプを意味すると仮定するつもりです。ただし、動的型については後ほど説明します。

静的型は、静的に証明できるプログラムの一部のプロパティです(静的とは「実行せずに」を意味します)。静的に型付けされた言語では、書くかどうかに関係なく、すべての式に型があります。たとえば、Cish "int x = a * b + c --d"では、a、b、c、およびdには型があり、a * bには型があり、a * b + cには型があり、a * b + c-dにはタイプがあります。ただし、xに型の注釈を付けただけです。 Scala、C#、Haskell、SML、F#などの他の言語では、それでも必要ありません。

どのプロパティが証明可能かは、タイプチェッカーによって異なります。

一方、Scalaスタイルクラスは、オブジェクトのセットの単なる仕様です。その仕様にはいくつかの型情報が含まれ、メソッド本体やプライベートフィールドなどの実装と表現の詳細が多数含まれています。Scalaでは、クラスはいくつかのモジュール境界も指定します。

多くの言語には型がありますがクラスがありません。また、多くの言語にはクラスがありますが(静的)型がありません。

タイプとクラスの間には、いくつかの観察可能な違いがあります。 List [String]は型ですが、クラスではありません。 Scalaでは、リストはクラスですが、通常はタイプではありません(実際にはより高い種類のタイプです)。 C#では、リストはどのような種類でもありません。Javaでは、「生の型」です。

Scalaは構造型を提供します。 {def foo:Bar}は、クラスに関係なく、Barを返すfooメソッドを持っている可能性のあるオブジェクトを意味します。これは型ですが、クラスではありません。

タイプは、タイプパラメータを使用して抽象化できます。 def foo [T](x:T)= ...と書くと、fooTの本体の中に型があります。しかし、Tはクラスではありません。

型はScala(つまり「抽象型メンバー」)で仮想化できますが、今日のScalaではクラスを仮想化できません(仮想クラスをエンコードするための定型的な重い方法がありますが- https://wiki.scala-lang.org/display/SIW/VirtualClassesDesign

さて、動的型。動的タイプは、ランタイムが特定の操作を実行する前に自動的にチェックするオブジェクトのプロパティです。動的に型付けされたクラスベースのOO言語では、型とクラスの間に強い相関関係があります。同じことがScalaやJavaなどのJVM言語でも起こります。これらの言語には、リフレクションやキャストなど、動的にしかチェックできない操作があります。これらの言語では、「型消去」は多かれ少なかれ、ほとんどのオブジェクトの動的型がそれらのクラスと同じであることを意味します。多かれ少なかれ。これは、たとえば、ランタイムがArray [Int]とArray [String]の違いを認識できるように、通常は消去されない配列には当てはまりません。しかし、私の広い定義「動的型は、ランタイムが自動的にチェックするオブジェクトのプロパティです」を覚えておいてください。リフレクションを使用すると、任意のオブジェクトに任意のメッセージを送信できます。オブジェクトがそのメッセージをサポートしている場合、すべてがうまくいきます。したがって、クラスではありませんが、動的タイプとしてアヒルのように鳴く可能性のあるすべてのオブジェクトについて話すことは理にかなっています。これが、PythonおよびRubyコミュニティが「ダックタイピング」と呼ぶものの本質です。また、私の広い定義によれば、「ゼロネス」でさえ、ほとんどの言語で、ランタイムが自動的に数値をチェックして、ゼロで除算しないことを確認するという意味で動的タイプです。ゼロ(またはゼロではない)を静的型にすることによって静的にそれを証明できる言語はごくわずかです。

最後に、他の人が述べたように、実装の詳細としてクラスを持たないintのようなタイプ、少し特別であるがクラスを持たないことができるNullやAnyのようなタイプ、およびクラスを持たないタイプがあります。クラスはもちろん、任意の値もあります。

44
James Iry

さて、私は噛みます...ジェームズは良い答えを持っているので、私は別のタクトを試して、より現実的な視点を与えるつもりです。

大まかに言えば、クラスはインスタンス化できるものです。シングルトンオブジェクト(scala)の特性(Scala)とインターフェイス(Scala)も、一般的にクラスと見なされます。シングルトンは(コンパイラで生成されたコードを介して)インスタンス化され、インターフェイスはサブクラスの一部としてインスタンス化できるため、これは理にかなっています。

これが2番目のポイントになります。クラスは、ほとんどのオブジェクト指向言語の設計の主要な単位です(ただし、javascriptのようなプロトタイプベースの言語ではありません)。ポリモーフィズムとサブクラス化は、どちらもクラスの観点から定義されています。クラスは、名前空間と可視性のコントロールも提供します。


タイプは非常に異なる獣であり、システムが表現できるすべての可能な値には1つ以上のタイプがあり、これらはクラスと同等である場合があります。たとえば、次のようになります。

(Int) => String // both the type and class are Function1[Int,String]
"hello world" // class and type are String    

また、ScalaとJavaの間にはいくつかの興味深い違いがあります:

7 // both the class and type are Int in Scala
  // in Java there's no class and the type is Integer.TYPE

println("hello world") // the return type is Unit, of class Unit
                       // Java has void as a type, but no corresponding class

error("oops") // the type and class are both "Nothing"

そして、クラスではない本当に楽しいタイプ。例えば、 this.typeは常にthisの一意のタイプを指します。これは単一のインスタンスに固有であり、同じクラスの他のインスタンスとの互換性すらありません。

抽象型と型パラメーターもあります。例えば:

type A // 'A' is an undetermined abstract type
       // to be made concrete in a subclass

class Seq[T] { ... } // T is a type, but not a class

Seqはクラスですが、型ではないので興味深いです。より正確には、これは「型コンストラクター」です。必要なtypeパラメーターが指定されたときに有効な型を構築するもの。型コンストラクターの別の用語は「より高い種類の型」です。「型コンストラクター」は、他の形式の引数と同じように型を提供するという観点から考えるように促します。これは、私に役立つメンタルモデルです。 Scalaの場合。

「より高い種類」とは、Seqに「種類」があることを意味します。つまり* => *、この表記は、Seqが単一の型を取り、単一の型を生成することを示します(これは、関数を記述するためのカレー表記に似ています)。比較として、Mapの種類は* => * => *2つのタイプパラメータを使用するため。

24
Kevin Wright

タイプは、インスタンスがなくても、それ自体で役立つ場合があります。その一例を「ファントムタイプ」といいます。 Javaの例を次に示します。 http://michid.wordpress.com/2008/08/13/type-safe-builder-pattern-in-Java/

その例では、public static class Initializer<HA, HB>、ここでHAHBは、インスタンス化されることなく、いくつかのタイプ(抽象クラ​​スTRUEFALSEで表されます)を取ります。

これが、型とクラスが何か違うこと、そして型自体が役立つことを示していることを願っています。

3
Landei

(Javaのみ)タイプはオブジェクトのセットです。 oがセットXのメンバーである場合、オブジェクトoはタイプXです。タイプXsubtype of Y、set X is subset of Y

すべてのクラスC(インターフェースではない)には、new C(...)から作成されたオブジェクトのセットがあります。興味深いことに、このセットを気にすることはめったにありません。 (しかし、すべてのオブジェクトはこのようなセットに属しています。これは役立つかもしれません)

すべてのクラスCには、タイプt(C)があり、一般に「タイプC」と呼ばれます。これは、new S(...)から作成できるすべてのオブジェクトのセットです。ここで、SはCです。またはCのサブクラス。

同様に、すべてのインターフェースIには、タイプt(I)、「タイプI」があります。これは、SがIを実装するnew S(...)から作成できるすべてのオブジェクトのセットです。

明らかに、クラスSCのサブクラスである場合、タイプSはタイプCのサブタイプです。インターフェースIについても同様です。

空のセットであるnullタイプがあります。 nullタイプは、すべてのタイプのサブタイプです。

タイプObjectであるすべてのオブジェクトのセットがあります。あらゆるタイプのスーパータイプです。

これまでのところ、この形式はかなり役に立たない。タイプは基本的にクラスまたはインターフェースと同じであり、サブタイプ関係は基本的にサブクラス/サブインターフェース関係です。些細なことは良いことです、言語は理解できました!しかし、ジェネリックスを入力すると、より複雑な型があり、型の和集合や交差などの操作があります。タイプはもはやクラスとインターフェースだけではなく、サブタイプの関係ははるかに豊富で理解しにくいものです。

1
irreputable