In Verse, a class is a template for creating objects with similar behaviors and properties. It is a composite type, which means that it’s bundled data of other types and functions that can operate on that data.
For example, let’s say you want to have multiple cats in your game. A cat has a name and an age, and they can meow. Your cat class could look like this:
cat := class:
Name : string
var Age : int = 0
Sound : string
Meow() : void = DisplayMessage(Sound)
Definitions of variables that are nested inside the class define fields of the class. Functions defined inside a class may also be called methods. Functions and methods are referred to as class members. In the above example, Sound
is a field, and Meow
is a method of cat
.
Fields of a class may or may not have a default value, or may only define a type that limits the values that the field may have. In the above example, Name does not have a default value, while Age does. The value may be specified using an expression that has the
For instance, lets say you want your cats to be able to tilt their heads. You can initialize an initial rotation HeadTilt
in the following code using the IdentityRotation()
method because it has the <converges>
specifier and is guaranteed to complete with no side effects.
cat := class:
...
# 有効な式
HeadTilt:rotation = IdentityRotation()
クラスを作成する
With a class that defines what a cat is and what the cat can do, you can construct an instance of the class from an archetype. An archetype defines the values of the class fields. For example, let’s make an old cat named Percy from the cat
class:
OldCat := cat{Name := â€Percyâ€, Age := 20, Sound:= â€Rrrrâ€}
この例のアーキタイプは {
と }
で囲まれた部分です。クラスのすべてのフィールドに対して値を定義する必要はありませんが、少なくともデフォルト値がないすべてのフィールドに値を定義する必要があります。いずれかのフィールドが省略されている場合、作成された インスタンス には、そのフィールドのデフォルト値が入ります。
In this case, the cat
class Age
field has a default value assigned to it of (0
). Since the field has a default value, you’re not required to provide a value for it when constructing an instance of the class. The field is a variable, which means that though you might provide a value at construction time, the value of that variable can be changed after construction.
一方、cat
の Name
フィールドは 変更できる 変数 ではなく、デフォルトで変更できないようになっています。これは、作成時にデフォルト値を指定できるものの作成後は変更できない、つまり、不変 であることを意味します。
Since a class in Verse is a template, you can make as many instances as you want from the cat
class. Let’s make a kitten named Flash:
Kitten := cat{Name := â€Flashâ€, Age := 1, Sound := â€Mewâ€}
フィールドにアクセスする
Now that you have some instances of cat
, you can access each cat’s Name
field with OldCat.Name
or Kitten.Name
, and call each cat's Meow
method with OldCat.Meow()
or Kitten.Meow()
.
どちらの cat にも同じ名前のフィールドがありますが、それらの値は異なります。たとえば、Sound
フィールドの値が異なるため、OldCat.Meow()
と Kitten.Meow()
の動作は異なります。
自分自身
Self
は、メソッドが 呼び出された クラスのインスタンスを参照するためにクラス メソッドで使用できる、Verse の特殊な識別子です。Self
を使用しなくてもメソッドが呼び出されたインスタンスの他のフィールドを参照することはできますが、インスタンス全体を参照する場合は Self
を使用する必要があります。
たとえば、次のように、DisplayMessage
でメッセージを関連付ける pet の引数が必要な場合がこれに該当します。
DisplayMessage(Pet:pet, Message:string) : void = …
cat := class:
…
Meow() : void = DisplayMessage(Self, Sound)
猫の鳴き声をより大きくした状態で初期化したい場合は、設定済みの Sound
変数をベースにすることを検討する可能性があります。ただし、これは次のコードでは機能しません。デフォルト値の式は識別子 Self
を使用できないため、LoudSound
がインスタンス メンバー Sound
を参照できないからです。
cat := class:
...
Sound : string
Meow() : void = DisplayMessage(Self, Sound)
# 次は、デフォルト クラスの値が Self を参照できないため、
# 失敗します。
LoudSound : string = "Loud " + Self.Sound
LoudMeow() : void = DisplayMessage(Self, LoudSound)
サブクラスと継承
クラスは、継承するクラスのスーパークラスのすべてのフィールドを含む、スーパークラス を 継承 できます。このようなクラスは、スーパークラス の サブクラス と呼ばれます。次に例を示します。
pet := class:
Name : string
var Age : int = 0
cat := class(pet):
Sound : string
Meow() : void = DisplayMessage(Self, Sound)
dog := class(pet):
Trick : string
DoTrick() : void = DisplayMessage(Self, Trick)
ここでは、cat
と dog
を定義するときに class(pet)
を使用することにより、cat と dog が pet
クラスを継承することを宣言します。つまり、cat と dog は pet
のサブクラスになります。
これには次のようないくつかのメリットがあります。
- Since both cats and dogs have names and ages, those fields only have to be defined once, in the
pet
class. Both thecat
and thedog
field will inherit those fields. - The
pet
class can be used as a type to refer to instance of any subclass ofpet
. For example, if you want to write a function that just needs the name of a pet, you can write the function once for both cats and dogs, and any otherpet
subclasses you might introduce in the future:IncreaseAge(Pet : pet) : void= set Pet.Age += 1
詳細については、「サブクラス」のページを参照してください。
オーバーライド
When you define a subclass, you can override fields defined in the superclass to make their type more specific, or change their default value. To do so, you must write the definition of the field in your subclass again, but with the <override>
specifier on its name. For example, you can add a Lives
field to pet
with a default value of 1, and override the default value for cats to be 9:
pet := class:
…
Lives : int = 1
cat := class(pet):
…
Lives<override> : int = 9
メソッド呼び出し
クラス インスタンスのフィールドにアクセスするときには、そのフィールドのインスタンスの値にアクセスします。 メソッドの場合、このフィールドは関数であり、オーバーライドするとフィールドの値が新しい関数に置き換えられます。 メソッドを呼び出すとフィールドの値が呼び出されます。 つまり、呼び出されるメソッドはインスタンスによって決まります。 ここでは、次の例について検討します。
pet := class:
…
OnHearName() : void = {}
cat := class(pet):
…
OnHearName<override>() : void = Meow()
dog := class(pet):
…
OnHearName<override>() : void = DoTrick()
CallFor(Pet:pet):void=
DisplayMessage("Yoo hoo {Pet.Name}!")
Pet.OnHearName()
CallFor(Percy)
を記述すると、cat
で定義されたとおりに OnHearName
メソッドが呼び出されます。Fido
が dog
クラスのインスタンスである CallFor(Fido)
を記述すると、dog
で定義されたとおりに OnHearName
メソッドが呼び出されます。
可視性指定子
クラスのフィールドとメソッドに 可視性指定子 を追加することにより、それらのフィールドとメソッドにアクセスするユーザーを制御できます。たとえば、所有しているクラスだけがプライベート フィールドにアクセスできるように、Sound
フィールドに private
指定子を追加することが可能です。
cat := class:
…
Sound<private> : string
MrSnuffles := cat{Sound := "Purr"}
MrSnuffles.Sound # Error: cannot access a private field
クラスで使用できるすべての可視性指定子を次に示します。
- public: Unrestricted access.
- internal: Access limited to current module. This is the default visibility.
- protected: Access limited to current class and any subclasses.
- private: Access limited to current class.
アクセス指定子
アクセス指定子をクラスに追加することで、そのクラスを構築できるユーザーを制御することができます。 これは、あるクラスのインスタンスを特定のスコープでのみ構築できるようにしたい場合などに便利です。
pets := module:
cat<public> := class<internal>:
Sound<public> : string = "Meow"
GetCatSound(InCat:pets.cat):string =
return InCat.Sound # Valid: References the cat class but does not call its constructor
MakeCat():void =
MyNewCat := pets.cat{} # Error: Invalid access of internal class constructor
cat
クラスのコンストラクタをそのモジュール pets
外で呼び出すと、class
キーワードが internal とマークされているため失敗します。これは、クラス識別子自体が public とマークされている場合も該当します。つまり、pets
モジュール外のコードから cat
を 参照 できます。
クラス キーワードで使用できるすべてのアクセス指定子を次に示します。
- public: Unrestricted access. This is the default access.
- internal: Access limited to the current module.
concrete 指定子
クラスに concrete
指定子がある場合、cat{}
などの空のアーキタイプでクラスを作成できます。つまり、このクラスのフィールドには必ずデフォルト値が必要です。さらに、具象クラスのサブクラスはすべて、それ自体が具象でなければなりません。
次に例を示します。
class1 := class<concrete>:
Property : int = 0
# Error: Property isn't initialized
class2 := class<concrete>:
Property : int
# Error: class3 must also have the <concrete> specifier since it inherits from class1
class3 := class(class1):
Property : int = 0
A concrete
class can only inherit directly from an abstract
class if both classes are defined in the same module. However, it does not hold transitively — a concrete
class can inherit directly from a second concrete
class in another module where that second concrete
class inherits directly from an abstract
class in its module.
unique 指定子
unique
指定子は、クラスに適用して一意のクラスにすることができます。unique クラスのインスタンスを作成するために、Verse は結果のインスタンスに一意の ID を割り当てます。これにより、unique クラスのインスタンスは、その ID を比較するだけで等しいかどうかを判断できます。unique
指定子がないクラスは、そのような ID がなく、等しいかどうかを判断するにはそのフィールドの値を比較する必要があります。
つまり、unique クラスは =
および <>
演算子で比較でき、comparable
型のサブタイプです。
次に例を示します。
unique_class := class<unique>:
Field : int
Main()<decides> : void =
X := unique_class{Field := 1}
X = X # X is equal to itself
Y := unique_class{Field := 1}
X <> Y # X and Y are unique and therefore not equal
final 指定子
final
指定子は、クラスとクラスのフィールドでのみ使用できます。
クラスに final
指定子がある場合、そのクラスのサブクラスを作成することはできません。次の例では、pet
クラスに final
指定子があるため、このクラスをスーパークラスとして使用することはできません。
pet := class<final>():
…
cat := class(pet): # Error: cannot subclass a “final†class
…
When a field has the final
specifier, you cannot override the field in a subclass. In the following example, the cat class can’t override the Owner
field, because the field has the final
specifier.
pet := class():
Owner<final> : string = “Andyâ€
cat := class(pet):
Owner<override> : string = “Sid†# Error: cannot override “final†field
When a method has the final specifier, you cannot override the method in a subclass. In the following example, the cat class can’t override the GetName()
method, because the method has the final specifier.
pet := class():
Name : string
GetName<final>() : string = Name
cat := class(pet):
…
GetName<override>() : string = # Error: cannot override “final†method
…
クラス本体のブロック式
ブロック式 はクラス 本体 で使用できます。クラスのインスタンスを作成するときには、block
式は定義された順番で実行されます。クラス本体の block
式で呼び出される関数では、NoRollback エフェクトを利用できません。
As an example, let’s add two block
expressions to the cat
class body and add the transacts effect specifier to the Meow()
method because the default effect for methods has the NoRollback effect.
cat := class():
Name : string
Age : int
Sound : string
Meow()<transacts> : void =
DisplayOnScreen(Sound)
block:
Self.Meow()
block:
Log(Self.Name)
OldCat := cat{Name := "Garfield", Age := 20, Sound := "Rrrr"}
When the instance of the cat
class, OldCat
, is created, the two block
expressions are executed: the cat will first say “Rrrrâ€; then “Garfield†will print to the output log.
インターフェース
インターフェース は、制限のあるクラスの形式で、値を持たないメソッドのみを含めることができます。クラスは他のクラスを 1 つしか継承できませんが、継承可能なインターフェースの数に制限はありません。