データ型のおさらい
まずは、データには型(type) があるということを、軽く復習しておきましょう。
なお、ここで取り上げるのは基本的な型であるプリミティブ型 です。(オブジェクト型については触れません)
さらに、特殊な型として扱われることが多いnull についても、ここでは触れません。
null許容とかnull安全(Null Safety)については、静的型付けとは少し話がズレますし、別の分野として捉えた方が理解しやすいかと思います。
上記を踏まえた上で、プログラミング言語にはどのようなデータの型があるのか、使用頻度が高いものを確認してみましょう。
プログラミング言語によって違いはありますが、概ね、プログラミング言語には予め次のようなデータの型が用意されています。
文字列(String)
文字(テキスト)。大抵、"
(ダブルクォーテーション)で囲む必要がある
整数(Int)
小数点を含まない数値
浮動小数(Double)
小数点を含む数値
真偽値(Boolean)
trueまたはfalseで表す
データ型が異なれば、同じ値(に見えるもの)でも、違うものとして扱われます。
例えば整数型の1と、浮動小数型の1.0は、異なるデータということになります。(JavaScriptではどちらもひっくるめてNumber型となっています)
同様に、文字列型の"
1"
と整数型の1、文字列型の"
true"
と、真偽値のtrueなども、別のものとして扱われるのが基本です。
しかし、正直なところ、JavaScriptではこれらの違いについてそこまで気をつけていなくても、プログラムコードは書けてしまいます。
JavaScriptでは、例え同一の変数であったとしても、どのようなデータ型でも代入できる仕組み(=動的型付け)になっている からです。
例えば、変数numberを宣言して、最初は整数型(JavaScriptにおいてはnumber:数値型)の数値を代入したとします。
let number = 5 ; // 数値(number)の5が代入される
console.log ( typeof ( number ) ) ; // number
▼
ここまでは別に何の問題もありませんね。
では次に、同じ変数に文字列型のデータ("
5"
)を代入してみましょう。
この時、JavaScriptでは特にエラーなど起こさず、データ型が異なる値をすんなり代入できてしまいます。
number = "5" ; // 文字列(string)の"5"が代入される
console.log ( typeof ( number ) ) ; // string
▼
【number】…なんて、いかにも数値型のデータが入っていそうな変数に、文字列型のデータが入ってしまいました。
この方が自由にコードを書けて便利だと感じる方もいるかもしれません。
実際、静的型付けの方が絶対に良いとか、静的型付け > 動的型付け という不等式が成り立つ優劣関係であるとか、そういうわけではなく、動的型付けだからこそのメリットもあります。
しかし、一般的には、このような仕様は混乱やバグの元となることが多い です。
numberと名付けられた変数があったら、多くの人は数値型のデータが格納されているはずだと予想するでしょう。
numberが数値型であることを利用して、他の数値と合算したり…といった機能を追加しようと考えるかもしれません。
ですが、実態は文字列型だった…となれば、当然ですが(そのままでは)他の数値と足したり引いたりといった四則演算を行うことはできないので、開発者は紛らわしい変数に苛立ちを覚えることは間違いないでしょう。笑
では、こんな時、変数numberに代入されているデータが数値型であることが保証 されていればどうでしょうか?
それなら安心してnumberを扱うことができそうですよね。
これを可能とする仕組みが、今回の記事のテーマである『静的型付け』 です。
次の章では、静的型付け言語における変数の宣言と値の代入例を見ていきましょう。
POINT!
・データ型には文字列、整数、真偽値などの型があり、それぞれ別のデータとして扱われる!
・JavaScriptでは、同じ変数に異なるデータ型の値を代入できてしまう!
・この方が便利な場合もあるが、一般的には混乱やバグを招いてしまいやすい!
静的型付け言語における変数宣言
この章では、静的型付け言語であるKotlin で変数を宣言し、その変数に値を代入する例を紹介しながら、静的型付けの特徴を掴んでいきたいと思います。
ちなみに、Kotlinとは主にAndroidアプリ開発に使われますが、Javaと互換性があるため、幅広い分野で活躍できるのが魅力的なプログラミング言語です。(筆者的に、書いていて一番楽しいプログラミング言語だと思います)
Kotlinのような静的型付け言語では、変数を宣言する際、どのようなデータ型を入れる変数であるのかを明記 します。(後で説明しますが、省略できる場合もあります)
var number : Int = 5
println (number :: class .simpleName ) // Int
▼
コードをよく見てみると、number変数の横に、: Int …という表記がありますね。
これがデータの型を宣言している部分であり、numberはInt型のデータが代入されていることが保証されます。
言い換えれば、numberには整数(Int)型以外のデータ型を入れることができません。
実際、文字列型のデータを代入しようとしてもコンパイルエラーになります。(エラーメッセージが表示され、コードを実行することができません)
var number : Int = 5
number = "5" // これはエラーになります
▼
ただ、データの型が保証されるのはありがたいとしても、変数を宣言する際にいちいちデータの型を明記するのは面倒くさいですよね。
そこで、Kotlinをはじめとした静的型付け言語では『型推論』 という仕組みが用意されていることが多く、わざわざ型を明記せずとも自動で型を指定してくれます。
例えば、変数xに1が代入されるのであれば、必然的にデータ型は整数型(Int)であることが推論されるので、xのデータ型はIntとなります。
具体的には次のように変数を宣言することが可能です。
var x = 1 // データ型は自動的にIntになる
▼
このように型推論を利用する場合も、データ型の宣言を省略できるというだけで、変数のデータ型が決まっていないわけではありません。
なので、Int型と推論されたxに文字列型のデータを代入しようとすると、やはりエラーになります。
var x = 5 // 型推論によってInt型となる
x = "5" // なので、これはエラーになります
▼
さらに、動的型付け言語のJavaScriptでは、とりあえず変数を宣言しておくだけ(値を代入しない)にしておき、その状態のまま変数を参照するということも可能ですが、静的型付け言語のKotlinでは、変数とデータ型を宣言しただけでは、その変数に指定されたデータ型の値が入っているとは言えないので、エラーになります。
let unknown ;
console.log ( typeof ( unknown ) ) ; // undefinedが出力され、エラーになるわけではない
▼
var integer : Int
print (integer ) // エラー:Variable 'integer' must be initialized
▼
このように、静的型付け言語では厳密なデータ型のチェックが行われるので、指定したデータ型ではないデータ型の値が代入されてしまったり、指定したデータ型の値の代入が忘れられたまま処理されてしまったり…ということが起こり得ません。
この特徴は、特に複数人で大規模なプロジェクトを開発する際に有利に働く と言えます。
一方、個人でちょっとしたものを開発・テストする程度であれば、静的型付けによる恩恵はそこまで感じられないかもしれません。
ですが、静的型付けのメリットは『指定したデータ型の値が入っていることが保証されること』だけではありません。
次の章では、個人開発においても『静的型付けって便利だな』 と思わせれてくれる特徴をご紹介します!
POINT!
・静的型付け言語では、変数に代入可能なデータの型が宣言時に決定される!
・型を明示的に宣言しなくても、型推論によってデータの型が決まる!
・型だけ宣言して変数を参照した場合も、エラーになる!
静的型付け言語における関数定義
個人開発でも、他の人が書いたコードを参考にしたり、ライブラリを利用したりすることはよくありますよね。
ですが、他人が書いたコードを読み解いたり、他人が作ったライブラリの仕様を理解したりするのは、一筋縄ではいきません。
言い換えれば、個人開発であっても『他人が書いたコードを見て理解できる』 ことは必須であり、避けては通れないということです。
であれば、他人の書いたコードが少しでも理解しやすいものであるに越したことはありませんよね?
そこで、活躍するのが『静的型付け言語における関数のパラメータ・戻り値の型チェック』 です。
動的型付け言語であるJavaScriptの場合、何か便利な処理をしてくれる関数があったとしても、その関数には何を引数として渡せば良いのか?とか、関数の処理によって最終的に返される値(戻り値)は何か?ということを知るには、関数の処理の中身を細かく確認しなければなりませんよね。
例えば、秒数(整数)を分に変換して、『○○秒は××分です』というメッセージ(文字列)を返してくれる関数 があったとしましょう。
この時、この関数に渡す引数は整数、戻り値は文字列ということになります。
文章で書くと当たり前じゃないかと思うかもしれませんが、関数で表記すると、意外とややこしいものです。
まずはJavaScriptでこの関数を表してみましょう。
function secondsToMinutes ( seconds ) {
return `${ seconds }秒は${ seconds / 60 }分です` ;
}
console.log ( secondsToMinutes ( 600 ) ) ; // 600秒は10分です
▼
この関数の処理を理解しようと思ったら、returnで返される値や処理の内容を確認・理解した上で、secondsにはどんな値を引数として渡せば良いのかを頭の中で整理する必要があります。
これぐらい処理の内容が簡単な関数でも、パッと見た瞬間に正確に把握できる人は少ないのではないでしょうか。
さらに、この関数の場合、関数の処理を勘違いしてしまって文字列型のデータを渡したとしてもエラーが起こるわけではないので、勘違いに気付けない可能性もなくはありません。
function secondsToMinutes ( seconds ) {
return `${ seconds }秒は${ seconds / 60 }分です` ;
}
console.log ( secondsToMinutes ( "600秒" ) ) ; // 600秒秒はNaN分です(エラーにはならない)
▼
パラメータに整数以外の値が渡された時にエラーや警告のメッセージを表示させたければ、関数内にメインの処理とは直接関係ない例外処理などを書かなくてはならなくなり、コードの記述量が増えてしまいます。
一方、静的型付け言語のKotlinであれば、関数のパラメータや戻り値のデータ型を指定する必要があるので、どんな値を引数として渡せば良いのか、そしてどんな値が返されるのかが一目で分かります。
fun secondsToMinutes (seconds : Int ): String {
return "${seconds }秒は${seconds / 60 }分です"
}
println (secondsToMinutes (600 )) // 600秒は10分です
▼
パラメータのsecondsには、: Int の表記により整数型を渡せば良いということがわかりますし、その後の :String の表記により、文字列型のデータが戻り値として返されることが明確になっていますね。
静的型付けにより、関数の中身を全く見ずとも、『引数として渡した整数をもとに、何らかの処理を行って文字列にしてくれる関数である』 ということが分かるようになっています。
当然、指定された型以外のデータ型を引数にして関数を実行しようとしてもエラーが起こるようになっているので、安心して使うことができます。
fun secondsToMinutes (seconds : Int ): String {
return "${seconds }秒は${seconds / 60 }分です"
}
println (secondsToMinutes ("600" )) // 文字列型を渡しているのでエラーになります
// Type mismatch: inferred type is String but Int was expected
▼
この違いにより、他人が書いたコードを読み解いて利用するという点においては、静的型付け言語の方に優位性がある と言えます。
前述したように、動的型付け言語より静的型付け言語の方が全てにおいて良い(完全上位互換)だと言いたいわけではないのですが、モダンなプログラミング言語は静的型付けの機能を備えていることが多く、時代の流れとしては静的型付けの方に傾いているのではないかと感じます。
だからこそ、本来はJavaScriptで全て事足りるにも関わらず、JavaScriptではなくTypeScriptが採用されたり…といった傾向が強まっているのではないでしょうか。
というわけで、ある程度JavaScriptに慣れて余裕が出てきたら、TypeScriptやその他の静的型付け言語の基本を学んでみるのもオススメですよ!きっと、世界が広がるはずです。
POINT!
・静的型付けでは、関数のパラメータや戻り値にもデータの型を明示する必要がある!
・なので、関数に渡す引数や戻り値のデータ型を一目で確認できる!
・静的>動的というわけではないが、静的型付けの特徴にも慣れておくと今後役に立つかも!