|
JavaFXプログラミング言語の基礎
JavaFX SDK 1.0 準拠
更新:08年12月28日
作成:08年12月11日
お願い:この文書は、Windows基本ソフトウェアの下でソフトウェア開発を行うことを前提に記述しています。Windows以外の基本ソフトウェアの下で開発を行う場合は、適宜読み替えを行ってください。
目次
1.JavaFXプログラミングを始める
1.1
JavaFXプログラム言語事始め
1.2
JavaFXプログラム開発用ソフトウェアを導入する
1.3
簡単なプログラムを作ってみる
2.変数
3.関数
4.クラスとオブジェクト
5.列
6.演算子
7.式
8.データ結合と引き金
9.パッケージ
10.利用修飾子
1.JavaFXプログラミングを始める
1.1 JavaFXプログラム言語事始め
JavaFXプログラム言語は、台本型言語(script
language)であるといわれます。台本型言語に厳密な定義はありませんが、その多くはプログラムが記述しやすく開発が早いなどの特徴を持っており、JavaFXもそれは同様です。
台本型言語で記述したコードを台本(script)といいます。台本を格納したファイルを台本ファイルといい、JavaFX言語の台本ファイルの場合は「.fx」という接尾辞が付きます。
ExampleOfFileName.fx
台本は式と宣言から構成されます。式(expression)は、変数、関数、定数、演算子を組み合わせたもので、演算規則に基づいて評価された値を持ちます(式の評価の概念は、数学におけるその概念と似ています)。宣言は、プログラムでどのような変数を用いるのかを、明示的に示すことです。
プログラミング言語における変数とは、計算機の記憶領域上に格納されるデータを、プログラムから利用できるように名前をつけたものです。
プログラミング言語における関数とは、ある特定の仕事を実行するコードの固まりに名前をつけたものです。関数の処理に必要なデータとして(明示的に)関数に渡す値と、それを関数内から利用できるように名前をつけたものを「引数」といいます。また、仕事を行った結果として得られた値をその関数の呼び出し側(その関数を引用しているコード部分)に返すとき、その値を関数の「戻り値」といいます。
また、プログラムのコード上に直に記した定数を、プログラミング言語では「直定数」(literal)と呼びます。
1.2 JavaFXプログラム開発用ソフトウェアを導入する
(1)JavaFX SDKを導入する
JavaFXプログラミング言語を用いてプログラムを開発するためには、JavaFX
SDK(JavaFX Software Development Kit:2008年12月11日現在の最新版は、JavaFX
1.0 SDKです)が必要になります。
JavaFX SDKには、「javafxc」というJavaFXプログラムの一括変換器(Compiler:プログラムの原始コード(人間が読める言葉で記述したプラグラム)を、計算機が実行できる形式に変換するソフトウェア)や、「javafx」というプログラム実行環境(runtime)など、ソフトウェア開発用の道具がいろいろと含まれています。
JavaFX 1.0 SDKは、JavaFX.comのStartページから、計算機に導入(install)することができます。
また、JavaFXプログラミング言語はJava言語をその基盤としていますので、JDK(Java
SE Development Kit:Java言語でプログラムを開発するためのソフトウェア)も導入しておく必要があります。JDK(2008年12月5日現在ではJDK
6 Update 11)を導入していない状態でJavaFX 1.0 SDKの導入作業を始めると、JDKを先ず導入するように促されますので、JavaFX
SDKと一緒にJDKも導入することができます。
(2)そのほかに必要なソフトウェア
この文書で例として取り上げるJavaFXプログラムの見本コードは、数行程度の小さいものばかりです。したがって、プログラムの作成には、文書編集器(Text
Editor。この文書では、Windowsの「メモ帳」を利用します)があれば十分です。
しかし、数十行のコードからなる少し大きなプログラムをいくつも作成するような場合には、IDE(Integrated
Development Environment:統合開発環境。文書編集器や一括変換器などの開発道具が統合されているので、こう呼ばれています)と呼ばれるソフトウェアを利用すると、効率的にプログラムを開発することができます。IDEに内蔵されている文書編集器では、作成中のプログラムに(プログラミング言語における)文法的な誤りがあると、編集中にそれを警告してくれます。そのため、プログラムを一括変換したときに見つかるような誤りや不具合の内、文法的に明らかなものは編集中に修正することができます。
また、NetBeansと呼ばれるIDEでは、プログラムの編集時に実行結果を事前に見ること(preview)ができますので、GUI(Graphical
User Interface:計算機の画面に表示された図形を介して、利用者と計算機が情報のやり取りを行う方式)用のプログラム開発において、実行結果をプラグラムにすばやく反映することができます。
NetBeansによるプログラム開発については、「NetBeansによるJavaFX
GUIプログラム作成」などを参照してください。
1.3 簡単なプログラムを作ってみる
(1)プログラムを書く
最初に、簡単なJavaFXプログラムのコードを書いてみましょう。Windowsのメモ帳を起動し、次の1行を入力して、「Hello.fx」というファイル名で適当なフォルダ(ディレクトリ)に保存してください。
println("こんにちは");
(2)プログラムを一括変換する
JavaFX言語は一括変換型言語ですので、実行に先立って、プログラムを計算機が処理できる形式のコードへ一括して変換しておく必要があります。
Windowsの「コマンド プロンプト」を起動し、Hello.fxファイルを保存したフォルダへ移動してください。フォルダ内にHello.fxファイルが格納されていることを確認したら、次のコマンドを入力して、Hello.fxを実行コードへ一括変換します(*1)。
javafxc
Hello.fx
変換が成功すると、同じフォルダに、次の2つのファイルが生成されているのが分かります。
hello.class
hello$Intf.class
hello.classがHello.fxファイルに対応した実行ファイル(JVM(Java
Virtual Machine:Java仮想機械)というJavaFXやJavaのプログラムの実行環境が、処理できる形式のコード(Javaバイトコードという)を格納したファイル)で、hello$Intf.classはその実行を支援するためのファイルです。hello$Intf.classファイルを直接いじることはありませんが、削除しないでください。
*1:「javafxc
hello.fx」と、小文字でファイル名を指定してもかまいません。
このことは、JavaFXの台本ファイルでは、英文字の大文字と小文字が
区別されないことも意味します。
(3)プログラムを実行する
最後に、次のコマンドを入力して、変換済みのコードを実行します。
javafx hello
すると、計算機の画面に次のように表示されます。
こんにちは
(4)プログラムの解説
プログラムの内容を簡単に説明します。
「println(…)」(*1)は関数の呼び出しを示し、その丸括弧内に指定された文字列(上記の例の場合は、その内の引用符で挟まれた部分)を、標準出力(この場合は、Windowsのメモ帳)に表示(して改行)します。その後に続くセミコロン(;)は、文の終わり(終端)を示します。
*1: println()関数は、JavaFXの処理系に組み込まれた関数で、単にそれを
呼び出すだけでその機能を利用することができます。
上記の例のように、JavaFXのプログラムは、簡潔に記述することが可能です(JavaFX言語が、台本型プログラミング言語と称されるゆえんでもあります)。定型的なことは、JavaFX言語の処理系に任せることができるので、プログラマはプログラム論理の構築作業に集中することができます。JavaFX言語は、RIA(Rich
Internet Application)プログラムの開発に威力を発揮する言語ですが、記述の簡潔さゆえに、初めてプログラミング言語を学ぶのにも適しています。
(5)説明文を記述する
プログラムには、説明文(comment)を挟むことができます。説明文の内容は任意ですが、決められた形式で記述しなければなりません。
JavaFX言語の一括変換器は、その形式に従って記述された説明文を無視し、コード変換の対象としません。JavaFXでは、次の3種類の形式で説明文を記述することができます。
1)// 説明文
一括変換器は、「//」からその行の終わり(行末)までを無視します。
2)/* 説明文
*/
一括変換器は、「/*」から「*/」までの部分を無視します。
この形式では、説明文を複数行に渡って記述することができます。
3)/** 説明文
*/
一括変換器は、「/**」から「*/」までの部分を無視します。
この形式の説明文も複数行に渡って記述することができます。
ちなみに、Java言語には「javadoc」と呼ばれる道具があり、この形式の説明文
からHTML形式のAPI(Application
Programming Interface)説明書などを
生成することができるため、文書化説明文(documentation
comment)と
呼ばれています。
それでは、前に作成したプログラムにこれらの説明文を追加して、次のように書き換えてみましょう。
/*
*
複数行に渡って記述できる説明文の例です。
*/
/**
*
文書化説明文の例です。
*/
// 行単位の説明文の例で、
// 記述が複数行にまたがることはできません。
/* この部分だけが無視されます*/
println("こんにちは");
前と同じように、次のコマンドを入力してプログラムを一括変換します。
javafxc
hello.fx
すると、一括変換が正常に終了することが分かります(何の誤り情報も表示されず、実行ファイルや実行支援ファイルが更新されていることが、正常に変換された印です)。
説明文がプログラムの一括変換において無視され、実行コードになんら影響を与えていないことを確認するため、次のコマンドを入力してプログラムを実行してみてください。
javafx hello
前と同様に、「こんにちは」と表示されます。
2.変数
(1)def変数とvar変数
変数は、var またはdef鍵語を用いて宣言します。
varを用いた変数は、次の形式で宣言し、宣言の後で値を割り当てなおすことができます。
var 変数名:
データ型 = 初期値;
defを用いた変数は、次の形式で宣言し、宣言時にのみ値が定義でき、後で割り当てなおすことができません。
def 変数名:
データ型 = 初期値;
データ型を明示的に指定しない場合、一括変換器が適切な型を推定してくれます(この機能を、型推定といいます)。データ型を指定しない場合は、その前のコロンも省略します。
var変数は、初期値を指定しないと、それぞれのデータ型で定められた既定値が割り当てられます(データ型も指定していない場合は、未定義であることを意味する特殊な値「null」が割り当てられます)。初期値を指定しない場合は、その前の等号も省略します。
def変数は、初期値の指定を省略することができません。def変数は、いわゆる定数のように使うことができますが、後に示すように、データ結合によりその値を変えることができます(def変数の定義を変えているわけではないため、再割り当てとはみなしません)。
変数名は、英字で始まる英数字列で表します。JavaFXでは、変数名の英字として小文字を用い、変数名が複数の単語から合成されるときは、次のように、2番目以降の単語の頭文字を大文字で表すのが慣例になっています
exampleOfVariableName
(2)データ型
JavaFXのデータ型は、式の値の種類を表します。
(a)String型
String型は、文字列を表します。文字列は、一重引用符または二重引用符で囲んで表すことができます(一重引用符または二重引用符は、それぞれ対で用いなければなりません)。一重引用符を用いても、二重引用符を用いても、それらに違いはありません。
var singleQuotedString:
String = 'こんにちは';
var doubleQuotedString:
String = "こんにちは";
String型の既定値(値の指定を省略したときに適用される、あらかじめ定められた値)は、空文字列(空の文字列、すなわち長さ0の文字列)です。
中括弧を用いることにより、文字列に式を埋め込むことができます。式の値は実行時に評価され、文字列表現に置き換えられます(例えば、数値の3は、数字の「3」になります)。
def name:
String = '一郎';
var greetingsSentence:
String = "こんにちは、{name}さん";
上の例で、greetingsSentenceの値は「こんにちは、一郎さん」となります。
次のように、複数行に渡って記述することもできます。
def greetingsSentence:
String = 'こんにちは、'
"一郎さん";
上の例も、greetingsSentenceの値は「こんにちは、一郎さん」となります。
文字列に一重引用符または二重引用符自体を含めるときは、文字列全体をそれぞれ二重引用符または一重引用符で囲みます。
var greetingsSentence:
String = "こんにちは、'一郎'さん";
greetingsSentenceの値は「こんにちは、'一郎'さん」となります。
また、半角の円記号(\)を用いて表す特殊文字を使用することもできます。
var greetingsSentence:
String = "こんにちは、\'一郎\"さん";
greetingsSentenceの値は「こんにちは、'一郎"さん」となります。
*:JavaFXでは、等号「=」は、その右側(右辺)の値をその左側(左辺)の変数
に割り当てることを表します。等価を表すわけではありません
(等価を表す記号(演算子)は別にあります)。
(b)Integer型
Integer型は、-2147483648から+2147483647までの整数値を表します。
def integralNumber:
Integer = 3;
Integer型の既定値は、0です。
8進数は、頭に0(ゼロ)をつけて表します。
012
16進数は、頭に0(ゼロ)xまたは0Xをつけて表します。
0x1a
AからFの数字は、大文字、小文字のどちらを用いてもかまいません。
(c)Number型
Number型は、浮動小数点数を表します。
def floatingPointNumber:
Number = 3.14;
指数形式の浮動小数点数の指数部は、大文字または小文字のEに続けて指定します。
31.4e-1
(d)Boolean型
Boolean型は、真偽値(trueまたはfalse)を表します。次の例は、booleanValueを真(true)に定義します(偽に定義するときは、falseを指定します)。
def booleanValue:
Boolean = true;
Boolean型の既定値は、falseです。
(e)Duration型
Duration型は、ミリ秒、秒、分または時間単位の時間直定数を表します。
5ms; //
5ミリ秒
10s; //
10秒
30m; //
30分
1h; //
1時間
(f)Void型
Void型は、式が値を持たないことを示します。
後に述べるwhile式はVoid型です。また、関数の場合は、関数が戻り値を返さないことを表します。
function
printNote(): Void {
println("この関数は戻り値を返しません");
}
3.関数
関数は、ある特定の仕事を実行するためのコードの固まり(区画)です。
関数は、次の形式で定義します。
function
関数名 (仮引数: データ型, 仮引数: データ型, ...):
戻り型 区画式
「仮引数」は、引数として関数に渡される値を、関数内で利用するための変数です。仮引数が複数個あるときは、それぞれをカンマで区切ります。戻り型は戻り値のデータ型を意味し、指定を省略すると、一括変換器が戻り値から適切なデータ型を推定してくれます。戻り値のデータ型の指定を省略するときは、その前のコロンも省略します。最後に指定する区画式は、関数の本体です。
次の例のように、型推定の機能により、仮引数のデータ型指定を省略することができる場合もあり。しかし、ほかの台本ファイルからもその関数を利用できるようにするためには、明示的にデータ型を指定しておかなければなりません。
say("こんにちは");
function
say(message) {
println(message);
}
関数の呼び出しは、関数定義の前にも後にもおくことができます(どちらを先に記述してもかまいません。上の例では、関数の呼び出しをその定義の前に行っています)。
関数名の命名規則とJavaFXでの命名法の慣例は、変数と同様です。なお、変数名には名詞を用い、関数名には動詞を用いることが多いようです。
getValue
(1)run()関数
この文書で最初に作成したHello.fxプログラムを、次のように書き換えてみましょう。
function
run(args: String[]) {
println("こんにちは、{args[0]}さん。");
}
そして、次のコマンドを入力して、前と同じように実行コードへ変換します。
javafxc
hello.fx
変換が終わったら、今度は次のようなコマンドを入力します(「一郎」のところには、お好きな名前を指定してみてください)。
javafx hello
一郎
すると、次のように画面に表示されます。
こんにちは、一郎さん。
今回のプログラムには、run()という新しい関数の定義が追加されています。run()関数は特別な関数で、実行対象の台本の入り口の役目を果たします。
run()関数は、String型のオブジェクトの列である引数argsを介して、コマンド行引数(コマンド名に続けて指定する1つ以上のデータで、上記のjavafxコマンドの場合は、その実行対象台本ファイル名(hello)に続けて指定した「一郎」の部分がそれにあたります)のデータを受け取ることができます。
最初に作成したHello.fxプログラムでは、run()関数を指定しませんでした。しかし、実際には一括変換器が暗黙の内に、引数を持たないrun()関数を生成し、その本体としてprintln()関数が実行されるように変換してくれていたのです。
4.クラスとオブジェクト
オブジェクト(object)は、あるもの(すなわち、object)の状態と振る舞いを表現するソフトウェア部品です。クラス(class)は、そのオブジェクトの原型を表します。
例えば、街を走っている実際の車はオブジェクトに、その車種はクラスにあたります。車の燃料の量や乗っている人の数などは、オブジェクトの状態にあたります。一方、左右に曲がることや速度を上げ下げすることなどは、振る舞いにあたります。
ソフトウェアでは、あるクラスからそのクラスに属するオブジェクトを生成する(具現化する)ことを、「実体化」(instancing)といいます。すなわち、オブジェクトは、クラスを実体化したものです。車の例でいえば、その車種の設計図から実際の車を製造することに当たります。
JavaFXでは、オブジェクトの状態は変数(*1)で、また振る舞いは関数(*2)で表し、それらを複合させた型としてクラスを定義します。
JavaFXには、あらかじめ定義されたクラスもありますが、利用者が独自のクラスを定義することもできます。
*1:JavaFXでは「実体変数」といいます。クラス内に定義された変数で、
クラス内からはその名前を指定するだけで利用できます。
*2:JavaFXでは「実体関数」といいます。クラス内に定義された関数で、
クラス内からはその名前を指定するだけで利用できます。
(1)クラスを一から定義する
クラスは、鍵語classを用いて、次の形式で定義します。
class クラス名
{クラスの本体}
クラスの本体部には、クラスに属す変数と関数を定義します。
1つの変数と1つの関数からなる簡単なクラスを定義してみましょう、
class Square
{
var
side: Number;
function
calcArea(): Number {
side
* side;
}
}
このクラスの名前は「Square」で、クラスのオブジェクト生成するときの型名として使用されます。クラスSquareには、Number型の変数sideと、Number型の(戻り値を返す)関数calcArea()が定義されています。
(2)ほかのクラスを継承する
クラスは、ほかのクラスを継承して定義することができます。「継承」とは、基になるクラス(「基本クラス」などと呼ばれます)から、その状態(JavaFXでは変数)と振る舞い(JavaFXでは関数)を引き継ぐことです。
基本クラスを継承してクラスを定義することを、「クラスを派生させる」といいます(このようにして定義したクラスは、基本クラスの「派生クラス」などと呼ばれます)。
派生クラスは、鍵語extendsを用いて、次の形式で定義します。
class クラス名
extends 基本クラス名 {クラスの本体}
上で定義したクラスSquareを継承して、クラスScaledSquareを定義してみましょう。
class ScaledSquare
extends Square {
var
scale: Number;
function
calcCircum(): Number {
4
* side * scale;
}
}
クラスScaledSquareはクラスSquareを継承(extends)していますので、Squareの変数sideと関数calcArea()を暗黙の内に持っています。それらに加えて、Number型の変数scaleとNumber型の関数calcCircum()を新たに定義しています。
したがって、クラスScaledSquareを実体化したオブジェクトでは、これら4つの変数と関数を利用することができます。
(3)クラスの関数を再定義する
派生クラスでは、基本クラスから継承した関数を再定義(override)することができます。
再定義する関数には、先頭に鍵語overrideをつけます。
クラスScaledSquareで、クラスSquareから継承した関数calcArea()を、定義しなおしてみましょう。
override
function calcArea(): Number {
side
* scale * side * scale;
}
(4)オブジェクト直定数を宣言する
JavaFXでは、オブジェクトはオブジェクト直定数として生成することができます。
例えば、上で定義したクラスScaledSquareのオブジェクトは、オブジェクト直定数により、次のようにして生成することができます。
ScaledSquare
{
side:
5
scale:
2
}
上の例では、オブジェクトの実体変数side、scaleの値を初期設定しています。各実体変数は、上記のような空白(半角空白、タブ、改行)文字、もしくはカンマまたはセミコロンのいずれで区切ってもかまいません。
また、次のtextReferenceとfontの例ように、生成したオブジェクトを変数に割り当てることもできますし、入れ子にすることもできます(この例では、「JavaFX
API」としてあらかじめ定義されているクラスのオブジェクトを生成しています)。
def textReference
= Text {
x:
10, y: 20
font:
Font {
size:
20
}
content:
"入れ子のオブジェクト直定数"
}
(5)オブジェクトの実体変数と実体関数を引用する
クラスの外からオブジェクトの実体変数と実体関数を利用するときは、そのオブジェクトを割り当てた変数に続けて、ドットと、呼び出したい変数や関数を指定します。
これまでの説明を基にして、クラスを定義し、そのオブジェクトを生成して、その実体変数や関数を利用するプログラムを作ってみましょう。次のプログラムを実行すると、
class Square
{
var
side: Number;
function
calcArea(): Number {
side
* side;
}
}
class ScaledSquare
extends Square {
var
scale: Number;
override
function calcArea(): Number {
side
* scale * side * scale;
}
function
calcCircum(): Number {
4
* side * scale;
}
}
def scaledSqr
= ScaledSquare {
side:
5
scale:
2
}
println("一辺{scaledSqr.side
* scaledSqr.scale}の正方形の");
println("面積は{scaledSqr.calcArea()}です");
println("外周は{scaledSqr.calcCircum()}です");
画面に次のように表示されます。
一辺10.0の正方形の
面積は100.0です
外周は40.0です
5.列
列(sequence)は、オブジェクトの順序付けられた並びです。列の要素は、項目(item)と呼ばれます。
列は、次のように、項目の並びを角括弧([])で囲んで表します。また、個々の項目はカンマで区切ります。
["水星",
"金星", "地球", "火星"]
(1)列を生成する
列を生成するひとつの方法は、各項目を明示的に並べることです。次の例は、上記の列を変数innerPlanetsに割り当てることを宣言します。
var innerPlanets
= ["水星", "金星", "地球",
"火星"];
変数のデータ型を明示するときは、次のようにデータ型名の後に角括弧[]をつけて表します。
var innerPlanets:
String[] = ["水星", "金星",
"地球", "火星"];
次のように、入れ子にして宣言することもできます。
var outerPlanets
= ["木星", "土星", ["天王星",
"海王星"]];
この場合、一括変換器は、次のように入れ子構造を平らにして1つの列にします。
var outerPlanets
= ["木星", "土星", "天王星",
"海王星"];
また、数列を形成する列を生成する場合は、次のような略記法もあります。
var numbers
= [11..15];
これは、次のように指定するのと同じです。
var numbers
= [11, 12, 13, 14, 15];
(2)論理式を含む列を生成する
論理式(述部)を用いることにより、既存の列の項目の部分集合からなる新たな列を宣言することができます。
例えば、
var numbers
= [11, 12, 13, 14, 15];
の内の、偶数だけからなる列を生成する場合は、次のように記述します。
var evenNumbers
= numbers[n | n mod 2 == 0];
新たな列(上記の例ではevenNumbers)を生成する際に、元の列(同numbers)の項目(同n。この変数名は任意です)が満たさなければならない論理式を、縦棒(|)に続けて指定します。上の例では、一括変換器は、evenNumbersに列[
12, 14 ]を割り当てます。
(3)列の項目を指定する
列の個々の項目は、列名の後に角括弧で囲まれた当該項目の添え字(0から始まる項目番号)を付けて指定します。
たとえば、次のプログラムを実行すると、
var innerPlanets
= ["水星", "金星", "地球",
"火星"];
println(innerPlanets[0]);
println(innerPlanets[3]);
次のように、画面に表示されます。
水星
火星
また、列の項目数は、sizeof演算子を使って得られます。次のプログラムを実行すると、画面に「4」と表示されます。
var innerPlanets
= ["水星", "金星", "地球",
"火星"];
println(sizeof
innerPlanets);
(4)列へ項目を挿入する
鍵語insertを使うと、列の特定の項目の前または後に項目を挿入することができます(実際には、一度生成した列(の項目や項目数)を変えることはできません。挿入を行った結果に相当する新たな列を生成して、列を参照している変数に割り当てなおすことにより、挿入を行ったのと同じ結果を得ることができるようになっています。後述の削除や反転も同様です)。
次のプログラムを実行すると、
var planets
= ["金星"];
println(planets);
insert "地球"
into planets;
println(planets);
insert "水星"
before planets[0];
println(planets);
insert "火星"
after planets[2];
println(planets);
画面に、次のように表示されます。
[
金星 ]
[
金星, 地球 ]
[
水星, 金星, 地球 ]
[
水星, 金星, 地球, 火星 ]
鍵語insertとintoを使うと、列の最後に挿入(追加)します。
insert
"地球" into planets;
鍵語insertとbeforeを使うと、列の指定した項目の前に挿入します。
insert "水星"
before planets[0];
鍵語insertとafterを使うと、列の指定した項目の後に挿入します。
insert "火星"
after planets[2];
(5)列から項目を削除する
鍵語deleteを使うと、列から項目を削除することができます。
次のプログラムを実行すると、
var planets
= ["水星", "金星", "地球",
"火星"];
println(planets);
delete "金星"
from planets;
println(planets);
delete planets[0];
println(planets);
delete planets;
println(planets);
画面に、次のように表示されます。
[ 水星,
金星, 地球, 火星 ]
[ 水星,
地球, 火星 ]
[ 地球,
火星 ]
[ ]
鍵語deleteとfromを使うと、指定した項目を列から削除します。
delete "金星"
from planets;
添え字を使って、削除する項目を指定することもできます(削除する範囲を「列名[m..n]」の形式で指定することもできます)。
delete planets[0];
列名だけを指定すると、列のすべての項目を削除します。
delete planets;
(6)列の項目並びを反転する
演算子reverseを使うと、列の項目並びを反転することができます。
ただし、前述したように、列そのものは不変です。したがって、反転した列を得るには、別の変数に割り当てなおす必要があります。
次のプログラムを実行すると、
var planets
= ["水星", "金星", "地球",
"火星"];
reverse
planets;
println(planets);
var reversedPlanets
= reverse planets;
println(reversedPlanets);
画面に次のように表示されます。
[ 水星,
金星, 地球, 火星 ]
[ 火星,
地球, 金星, 水星 ]
(7)等価な列
列は、その項目の数が同じでかつ個々の項目が同一の場合に、等価となります。
(8)列断片を指定する
次の形式で列の添え字の範囲を指定すると、列断片(列の一部)を表すことができます。
[初項 ..
< 終項]
「小なり」記号(<)は、終項を含まないことを表します。後述する値域式の形式と似ていますが、値域式のように公差(step)を指定することはできません。
次のプログラムを実行すると、
var planets
= ["水星", "金星", "地球",
"火星"];
println(planets[1..2]);
println(planets[0..<2]);
println(planets[1..]);
println(planets[0..<]);
画面に次のように表示されます。
[ 金星,
地球 ]
[ 水星,
金星 ]
[ 金星,
地球, 火星 ]
[ 水星,
金星, 地球 ]
添え字[m..n]は、列の添え字mから添え字nまでの項目を指定します。
planets[1..2]
添え字[m..<n]は、列の添え字mから添え字nの1つ前(n-1)までの項目を指定します。
planets[0..<2]
添え字[m..]は、列の添え字mから最後までの項目を指定します。
planets[1..]
添え字[m..<]は、列の添え字mから最後の1つ前までの項目を指定します。
planets[0..<]
6.演算子
(1)代入演算子
代入演算子「=」は、(これまで見たきたように)演算子の右辺の値をその左辺に割り当てます。
(2)算術演算子
算術演算子は、加算、減算、積算、除算、剰余算を実行します。
算術演算子
意味
+ 加算
- 減算
* 積算
/ 除算
mod 剰余算
(3)複合代入演算子
算術演算子を代入演算子と組み合わせて、複合代入演算子とすることができます。複合代入演算子の左辺の値(算術演算の被演算数かつ代入演算の被演算数)と右辺の値(算術演算の演算数)を算術演算し、その結果を左辺に代入します。
複合代入演算子
意味
+= 加算して代入する
-= 減算して代入する
*= 積算して代入する
/= 除算して代入する
例えば、「n += 3;」は、「n = n +
3;」と記述するのと等価です(ほかも同様です)。
ただし、剰余算を表すmodは、代入演算子と組み合わせることはできません(すなわち、「n
= n mod 2;」を「n mod= 2;」と書くことはできません)。
(4)単項演算子
算術演算子は、被演算数と演算数の2つの値をとりますが、単項演算子は被演算数のみをとります。
単項演算子
意味
- 数の正負を反転する
++ 1増(1だけ増やす)
-- 1減(1だけ減らす)
not 論理否定(真偽値を反転する)
例えば、次のプログラム実行すると
var booleanValue
= false;
println(booleanValue);
println(not
booleanValue);
画面に次のように表示されます。
false
true
1増演算子(++)と1減演算子(--)は、被演算数の前(前置)または後(後置)に置くことができます。「++n;」または「n++;」のように被演算数との組み合わせで単体で用いる分には、両者に違いはありません。しかしながら、被演算数の値を引用する場合は、増減後の値に評価される(前置)か、増減前の値に評価される(後置)かという違いがあります(「演算子と被演算数が並んでいる順に評価される」と、覚えてください)。
例えば、次のプログラムを実行すると、
var n =
3;
++n;
println(n);
n++;
println(n);
var m =
++n;
println(m);
m = n++;
println(m);
println(n);
画面に次のように表示されます。
4
5
6
6
7
(5)等価演算子と関係演算子
等価演算子と関係演算子は、演算子の左辺と右辺の大小関係を判定します。その結果、条件を満たせば、Boolean型のtrueに、そうでなければfalseになります。
演算子 意味
== 等価
!= 不等価
> 大なり
>= 以上
< 小なり
<= 以下
例えば、次のプログラムを実行すると、
var n =
3;
println(n
== 3);
println(n
!= 3);
println(n
> 3);
println(n
>= 3);
println(n
< 3);
println(n
<= 3);
次のように画面に表示されます。
true
false
false
true
false
true
(6)論理演算子
論理演算子「and」と「or」は、それぞれ、2つの論理式の論理積と論理和を実行します。
例えば、次のプログラムを実行すると、
var n =
3;
var m =
5;
println((n
== 3) and (m == 5));
println((n
!= 3) or (m != 5));
次のように画面に表示されます。
true
false
(7)型比較演算子
型比較演算子「instanceof」は、オブジェクトが指定した型の実体(instance)かどうかを判定します。
例えば、次のプログラムを実行すると、
def hello
= "こんにちは";
println(hello
instanceof String);
画面に「true」と表示されます。
(8)型変換演算子
型変換演算子「as」は、オブジェクトを指定した型に変換します。
例えば、次のプログラムを実行すると、
var n =
1;
println(n);
var m =
n as Number;
println(m);
次のように画面に表示されます。
1
1.0
7.式
(1)区画式
区画式は、中括弧に囲まれ、セミコロンで区切られた宣言または式の羅列から構成されます。区画の最後の式の値が、その区画式そのものの値となります。
区画式が式を含まないときは、Void型となります。
例えば、次のプログラムを実行すると、
var result
= {
var
numberOne = 2;
var
numberTwo = 3;
var
sum = numberOne + numberTwo;
}
println(result);
画面に「5」と表示され、変数resultに区画式の値として、区画の最後の式「var
sum = numberOne + numberTwo;」の値5が代入されていることが分かります。
(関数の本体内を含め)区画内で定義された変数を局所変数(local
variable)といいます。局所変数は、その変数が定義された区画内のみから利用することができます。局所変数には、後に述べる利用修飾子を指定することができません。
関数には、局所変数に相当するもの(局所関数)はありません。
(2)if式
if式は、指定した条件が満たされたときのみコードの区画を実行するように、プログラムの流れを制御します。
ifの後の丸括弧の中の式の値を評価し、値がtrueのときは「then」の後の式を実行し、falseのときは「else」の後の式を実行します。thenは、省略することができます。また、評価結果がfalse
でelse部分がないときは、そのままif式の次の式に実行が移ります。
例えば、次のプログラムを実行すると、
def n =
5;
var message;
if (n <
3) then {
message
= "小";
} else if
(n > 7) {
message
= "大";
} else {
message
= "中";
}
println(message);
画面に、「中」と表示されます。
上のプログラムは、次のように、簡便な形式(「条件式」といいます)で表すこともできます。
def n =
5;
var message
= if (n < 3) "小" else if (n > 7)
"大" else "中";
println(message);
(3)値域式
値域式は、Integer型またはNumber型の等差数列からなる列を表します。値域式は、等差数列の初項、終項および公差を、角括弧で囲んで次の形式で記述します。
[初項 ..
< 終項 step 公差]
「小なり」記号「<」は、終項を含まないことを表します。また、公差の既定値は1です。
例えば、[11..15]は、11から15までの整数列を表します。次のプログラムを実行すると、
var evens
= [12..14 step 2];
println(evens);
evens =
[12..<14 step 2];
println(evens);
次のように画面に表示されます。
[ 12, 14
]
[ 12 ]
降順(大から小の順)の値域式を生成する場合は、初項の値より小さな終項を指定し、負の公差を指定します。例えば、次のプログラムを実行すると、
var evens
= [16.0 .. 12.0 step -2.0];
println(evens);
画面に「[ 16.0, 14.0, 12.0 ]」と表示されます。
(4)for式
for式は、指定した列(「入力列」といいます)の各項目について、式を繰り返し実行します。
for (項目in
入力列) 式
例えば、次のプログラムを実行すると、
for (i in
[1..3]) println("こんにちは");
画面に、次のように表示されます(println()関数が繰り返し実行されます)。
こんにちは
こんにちは
こんにちは
また、繰り返し実行される式の中で、列の各項目を参照することもできます。例えば、次のプログラムを実行すると、
var numbers
= [1..3];
println(for
(n in numbers) n * n);
画面に、「[ 1, 4, 9 ]」と表示されます。上の例では、入力列に列の参照変数を指定しています。
(5)while式
while式は、指定した条件が満たされなくなるまで、式を繰り返し実行します。
while (条件)
式
例えば、次のプログラムを実行すると、
var i =
0;
while (i
< 3) {
i++;
println("{i}回目");
}
画面に、次のように表示されます。
1回目
2回目
3回目
(6)break式とcontinue式
break式とcontinue式は、for式やwhile式に関連して用いられます。break式は、繰り返しそのものを中止します。一方、continue式は、繰り返しのその回のみを中止します(残りの回の繰り返しは、続行されます)。
break式とcontinue式はともにVoid型で、値を返しません。
例えば、次のプログラムを実行すると、
for (i in
[1..10]) {
if
(i > 5) {
break;
}
if
(i mod 2 == 0) {
continue;
}
println("{i}回目");
}
画面に、次のように表示されます。
1回目
3回目
5回目
(7)new式
new式は次の形式で指定し、Javaのクラスの実体を生成します。
new Javaクラスの型名(構築子への引数)
次の例のように、利用するJavaクラスを台本に取り込むためのimport宣言(後述します)が必要になります。
import java.util.Date;
...
var now
= new Date();
先述したJavaFX APIとして定義されたクラスのオブジェクトを生成する場合も、所要クラスのimport宣言が必要になります。
8.データ結合と引き金
鍵語bindは、対象となる変数の値を指定した式の値に結合します。指定した式の値が変化すると、対象となる変数の値も自動的に更新されます。
結合される式は、単純な基本型の値、オブジェクト、関数の戻り値、または式の値のいずれでもかまいません。
(1)変数やオブジェクトに結合する
先ず、次のプログラムを実行してみましょう。
var x =
3;
def y =
bind x;
println(y);
x = 5;
println(y);
x = 7;
println(y);
すると、次のように画面に表示されます。
3
5
7
上の例では、変数yは変数xに結合すると定義している(「def
y = bind x;」)ため、変数xの値が変わると、変数yの値も自動的に更新されます(前にも述べましたが、def変数は再割り当てできませんが、結合によりその値を変えることができます)。
次に、オブジェクトに結合させる例を見てみましょう。次のプログラムを実行すると、
class Point
{
var
x: Number;
var
y: Number;
}
var aX =
3.0;
def aPoint
= bind Point {
x:
aX
y:
5.0
}
println("x座標
= {aPoint.x}");
aX = 7.0;
println("x座標
= {aPoint.x}");
次のように画面に表示されます。
x座標 =
3.0
x座標 =
7.0
変数aPointはオブジェクトPointに結合されているため、変数aXの値を変えると、aPointが参照しているオブジェクトの実体変数xの値も変わります。
上の例の場合は、変数aXの値を変えると新たなPointオブジェクトが生成されて、変数aPointに再割り当てされます。新たなPointオブジェクトを生成することなく、変数aXの変化の跡を追うようにするには、次のようにaPointを定義します。
def aPoint
= bind Point {
x:
bind aX
y:
5.0
}
変数aYを別途定義し、aPointが参照しているオブジェクトの実体変数yも、同様にしてaYの変化の跡を追うようにする場合は、次のようにaPointを定義します。
def aPoint
= Point {
x:
bind aX
y:
bind aY
}
(2)関数に結合する
関数への結合には、非結合関数への結合と結合関数への結合の2種類があります。
非結合関数への結合では、関数の引数が変化したときのみ関数が評価しなおされます。一方、結合関数への結合では、引数以外に関数本体の式の値が変化したときも関数が評価しなおされます。
結合関数を定義する場合は、鍵語functionの前に鍵語boundを指定します(boundがない場合の定義は、非結合関数の定義となります)。結合関数として定義した関数も、通常の関数と同じように、結合とは無関係に呼び出すことができます(通常の関数と同様、関数の戻り値を割り当てた変数の値は、自動更新されません)。
先ず、非結合関数に結合させる例を見てみましょう。次のプログラムを実行すると、
class Point
{
var
x: Number;
var
y: Number;
}
var distance
= 10.0;
function
translate(currentX: Number, currentY: Number): Point
{
Point
{
x:
currentX + distance
y:
currentY + distance
}
}
var aX =
3.0;
var aY =
5.0;
def aPoint
= bind translate(aX, aY);
println(aPoint.x);
aX = 7.0;
println(aPoint.x);
distance
= 20.0;
println(aPoint.x);
次のように画面に表示され、関数translate()の引数aXが変化したときのみ、aPointが参照しているオブジェクトの実体変数xの値が更新されているのが分かります。
13.0
17.0
17.0
次に、上の関数translate()を結合関数に書き換えて実行してみます(鍵語functionの前に、鍵語boundを追加しています)。
class Point
{
var
x: Number;
var
y: Number;
}
var distance
= 10.0;
bound function
translate(currentX: Number, currentY: Number): Point
{
Point
{
x:
currentX + distance
y:
currentY + distance
}
}
var aX =
3.0;
var aY =
5.0;
def aPoint
= bind translate(aX, aY);
println(aPoint.x);
aX = 7.0;
println(aPoint.x);
distance
= 20.0;
println(aPoint.x);
こんどは、次のように画面に表示され、引数aXのほかに、関数本体内で引用している変数distanceの値が変化したときも、aPointが参照しているオブジェクトの実体変数xの値が更新されていることが分かります。
13.0
17.0
27.0
(3)for式に結合する
for式に結合する例を見てみましょう。次のプログラムを実行すると、
var originals
= [3..5];
def squares
= bind for (i in originals) i * i;
println(squares);
insert 6
into originals;
println(squares);
画面に、次のように表示され、結合されたfor式の入力式の項目が変化すると、結合対象となる変数が参照している列の項目も、自動的に更新されることが分かります。
[
9, 16, 25 ]
[
9, 16, 25, 36 ]
(4)双方向結合
双方向結合では、通常の結合による更新の流れと、その逆方向の更新の流れがともに可能になります。すなわち、結合対象の変数が変化すると、結合されている変数も変化します。
双方向結合の例を見てみましょう。次のプログラムを実行すると、
var x =
3;
var y =
bind x with inverse;
println("x
= {x}, y = {y}");
x = 5;
println("x
= {x}, y = {y}");
x = 7;
println("x
= {x}, y = {y}");
y = 9;
println("x
= {x}, y = {y}");
次のように画面に表示され、変数yの値が変化したときに変数xの値も変化し、双方向に結合されていることが分かります。
x = 3, y
= 3
x = 5, y
= 5
x = 7, y
= 7
x = 9, y
= 9
(5)置換引き金
置換引き金は、節「on replace」を用いて表し、引き金を取り付けた変数の値が変化したときに任意の区画式が実行される仕組みです(構文はちょっと複雑ですので、実際の例で示します)。
実行される式の中で、変化前の変数の値を参照することができます。また、変数の値の変化には、初期値の割り当ても含まれます。
次のプログラムを実行すると、
var x =
3 on replace oldValue {
println("xを{oldValue}から{x}に置換")
}
x = 5;
次のように画面に表示され、初期化(「var
x = 3」)時を含め、変数の値が変化したときに、置換引き金が実行されていることが分かります。式の中で変数の変化前の値を引用するため、区画式の前に旧値を参照する変数(oldValue。この変数名は任意です)を指定しています。
xを0から3に置換
xを3から5に置換
次に、列に置換引き金を適用してみましょう。次のプログラムを実行すると、
var days
= ["月", "水", "金"]
on replace seq[i1..i2] = item {
println("{seq}の[{i1}..{i2}]が\'{item}\'に置換されて{days}になった")
}
delete "水"
from days;
insert "土"
into days;
days[1..2]
= ["火","木"];
次のように画面に表示され、列のどの項目が置換されたかが分かります。
の[0..-1]が'月水金'に置換されて月水金になった
月水金の[1..1]が''に置換されて月金になった
月金の[2..1]が'土'に置換されて月金土になった
月金土の[1..2]が'火木'に置換されて月火木になった
9.パッケージ
パッケージは、1つ以上のコードファイルを集めて、名前を付けたものです。パッケージとコードファイルの関係は、Windowsなどのファイルシステムのフォルダ(ディレクトリ)とそこに収められているファイルの関係になぞらえることができます。
実際にも、パッケージ名と同じ名前のフォルダを作成し、そこにコードファイルを格納します。フォルダが階層構造をなすことができるように、パッケージも階層構造をとることができます。
パッケージを利用すると、作成したコードをその機能に基づいて分類することができ、さらには、コード間で名前の衝突が起こる(異なるものに同じ名前を付けることにより、参照関係を特定できなくなる)のを防ぐことができます。
*:異なるパッケージに属すコードでは、変数名や関数名などに同じ名前を
使うことができます。これは、パッケージごとに「名前空間」を別に
定義するためです。名前空間は、表現可能なすべての名前の集合と
思ってください。属す名前空間が異なれば、名前の衝突は起こりません。
(1)フォルダ(ディレクトリ)を作る
パッケージ名は、英字で始まる英数字列で指定します。英字は、複数の単語から構成される名前の場合も、(「exampleofname」のように)小文字だけで表すのが慣例になっています。
パッケージの名前を決めたら、先ず、ファイルシステム上に、パッケージ名と同じ名前のフォルダを作成します。ここでは、最初に作ったプログラムHello.fxを、パッケージ「greeting」に収める例を示します。
まず、greetingというフォルダを作って、その下にHello.fxファイルを移します。
(2)パッケージ宣言を追加する
各プログラムがどのパッケージに属すかは、それぞれのプログラム内で指定します。そのため、プログラムにパッケージ宣言を追加します。パッケージ宣言は、次の形式で記述します。
package
パッケージ名;
パッケージ宣言は、説明文を除き、必ずプログラムの1行目になければなりません。
上の例で、フォルダgreetingに格納したHello.fxを開き、1行目に次のようにパッケージ宣言を追加します。
package
greeting;
println("こんにちは");
(3)一括変換して実行する
上の例で、原始ファイルを一括変換するときは、親フォルダ(フォルダgreetingを格納したフォルダ)に移動してから、次のコマンドを入力します。
javafxc
greeting\hello.fx
変換したコードを実行するときは、次のように、パッケージ名とともに実行ファイルを指定します(パッケージ名とファイル名は、ドットで区切ります)。
javafx greeting.hello
パッケージ名に英大文字を使用した場合は、大文字と小文字を区別してパッケージを指定しないと、誤りになって実行できません。例えば、パッケージ名とフォルダ名を「Greeting」とした場合は、次のように指定します。
javafx Greeting.hello
10.利用修飾子
利用修飾子(access modifier)は、変数、関数、クラスなどを、どの範囲(クラス、台本、パッケージのそれぞれの階層で見たときの範囲)にあるコードから利用可能にするかを指定します。
利用可能な範囲を制限することにより、名前の衝突を防げるだけでなく、意図に反する利用によって生ずる不具合からプログラムを守ることができます。
(1)既定の利用可能範囲
既定の利用可能範囲は「同じ台本内」で、次のように、利用修飾子を指定しないときに利用可能な範囲です。
var x;
この利用可能範囲を持った変数(*1)、関数(*2)、クラスは、同じ台本ファイル内のコードからのみ初期化、再定義、読み取り、結合などの利用が可能です。
*1:同じ台本内からのみ利用可能なので、「台本変数」といいます。
*2:同じく「台本関数」といいます。
(2)package利用修飾子
変数、関数、クラスを同じパッケージに属するほかの台本ファイルからも利用できるようにするには、「package」利用修飾子を指定します。
package
var x;
パッケージ宣言と同じ鍵語packageを使用しますが、混同しないでください。
(3)protected利用修飾子
変数、関数を同じパッケージに属するほかの台本ファイルからとともに、任意のパッケージに属する派生クラスからも利用できるようにするには、「protected」利用修飾子を指定します。
protected
var x;
この利用修飾子は、クラスには適用できません。
(4)public利用修飾子
変数、関数、クラスを、任意のクラス、台本、パッケージ内のコードから利用できるようにするには、「public」利用修飾子を指定します。
public var
x;
(5)public-read利用修飾子
public-read利用修飾子は、任意のクラス、台本、パッケージ内のコードから読み取れるが、書き込みは同じ台本内のコードからのみ行えるような変数を定義します。
public-read
var x;
書き込みを行える範囲を拡大するには、次のように、packageやprotectedを前置します。
package
public-read var x;
protected
public-read var x;
それぞれ、package利用修飾子とprotected利用修飾子を指定した場合に利用可能な範囲のコードからも、書き込めるようになります。
(6)public-init利用修飾子
public-init利用修飾子は、任意のクラス、台本、パッケージ内のコードから読み取りまたはオブジェクト直定数による初期化はできるが、書き込みは同じ台本内のコードからのみ行えるような変数を定義します。
public-init
var x;
書き込みを行える範囲を拡大するには、public-readと同様に、packageやprotectedを前置します。
package
public-init var x;
protected
public-init var x;
それぞれ、package利用修飾子とprotected利用修飾子を指定した場合に利用可能な範囲のコードからも、書き込めるようになります。
(7)run()関数
利用修飾子を指定した(すなわち、既定の台本内利用可能な範囲ではない)変数、関数を、それらと同じ台本内のコードから利用する場合、明示的にrun()関数を定義し、その中から利用しなければなりません。
次のプログラムで、run()関数を定義しないで変数messageや関数sayHello()を利用すると、一括変換で誤りになります。
package
greeting;
public var
message = "こんにちは!";
public function
sayHello() {
println("関数から:{message}");
}
public class
Hello { //
クラスはpublicでも、
public
function say() { // publicがないと台本内のみ利用可になる
println("オブジェクトから:{message}");
}
}
var hello
= Hello{};
function
run(args: String[]) { // *省略できない*
println("同じ台本から:{message}");
sayHello();
hello.say();
} //
*省略できない*
「var hello = Hello{};」のように、クラスはrun()関数の外で参照しても誤りになりません。上のプログラムを実行すると、次のように表示されます。
同じ台本から:こんにちは!
関数から:こんにちは!
オブジェクトから:こんにちは!
(8)import宣言
利用修飾子を指定して、台本の外からも利用できるように宣言した変数、関数、クラスを別の台本内のコードから実際に利用する場合、import宣言によりそれらを台本内に取り込まなければなりません(import宣言により、一括変換器は、それらの利用を認識します)。
import宣言は、package宣言を除き、すべての式や宣言の先頭になければなりません(package宣言がある場合は、その後に指定します)。import宣言が複数(行)ある場合、それらの順番は任意です。
実際の例で見てみましょう。「(7)run()関数」に示したプログラムを、フォルダ(ディレクトリ)greetingの下にHello.fxというファイル名で格納して一括変換します。次に同じフォルダに次のプログラムを格納し、一括変換して実行すると、
package
greeting;
import greeting.Hello.*;
//import
greeting.hello.*; // 台本名は大文字小文字を区別しないとNG
var helloInTest
= Hello{};
//var helloInTest
= greeting.Hello{}; // importしない場合
function
run(args: String[]) {
println("別の台本から:{message}");
// println("別の台本から:{greeting.Hello.message}");
//
importしない場合
sayHello();
// greeting.Hello.sayHello();
// importしない場合
helloInTest.say();
}
次のように画面に表示されます。
別の台本から:こんにちは!
関数から:こんにちは!
オブジェクトから:こんにちは!
import宣言は、「パッケージ名.台本名.*」の形式で記述します。
import greeting.Hello.*;
このとき、パッケージ名や台本名の英文字は、大文字と小文字を区別して指定しなければなりません。この例では、次のように宣言すると誤りになります。
import greeting.hello.*;
import宣言を行わない場合は、次のように、個々にパッケージ名と台本名を付けて指定します。
var helloInTest
= greeting.Hello{}; // 台本名とクラス名が同じ場合の例
println("別の台本から:{greeting.Hello.message}");
greeting.Hello.sayHello();
この例のように、台本名とクラス名が同じ場合、個々にパッケージ名から指定する場合のクラス名は省略します。台本名とクラス名が異なる場合は、次のように、台本名とクラス名を指定します(台本名を「HelloJpn」とした場合)。
var helloInTest
= greetingeng.HelloJpn.Hello{};
また、前の項に載せたプログラムの説明文にも書きましたが、クラスをpublicと宣言していても、その実体関数をpublicと宣言しないと、台本の外からは利用できません(クラスとその実体変数や実体関数の利用修飾子は、個々に指定する必要があります)。
public class
Hello { //
クラスはpublicでも、
public
function say() { // publicがないと台本内のみ利用可になる
|