[Kotlin]静的型付けと動的型付けの違い / Kotlinの型推論

今回は前回お話したKotlinの変数や定数宣言を使って、静的型付け言語と動的型付け言語の違いや型推論についてお話していこうと思います。

前回の記事はこちらですが、基本の型と変数/定数宣言の方法を理解されていれば、十分理解可能かと思います。

[Kotlin]基本の型について / 変数や定数を宣言する方法
今回はKotlin(というかプログラミング)の基礎の基礎、型と変数/定数について...
スポンサーリンク

静的型付けとは?

では変数/定数宣言したKotlinと同じことをJavaのコードで見ていきましょう。前回使用したコードをJavaコードにそのまま書き写すとこうなります。(クラス名は便宜上Mainにしてます)

public class Main {
public static void main(String[] args) {
  int num = 10;
  final String game = "Minecraft";
  System.out.println(game+"は"+num+"周年");
}
}

//Minecraftは10周年

Javaは静的型付け言語です。静的型付け言語では変数/定数に値を代入する場合、一度宣言した変数の型は原則変更できません。

だからこそ変数宣言をするときは「これはint型の変数です。これ以降intしか入れません」と同時に宣言する必要があります。

(Java10以降若干仕様が変更になっていますが、基本的には変わりません。変更になっている部分については後述します)

Javaではいたる所に型指定が必要

これは定数でも同じです。頭にfinalを付ければ定数になりますが他の部分に違いは無く、「String型のgameという定数です」という宣言をしなければなりません。

ついでに言えばこのコードの関数名の左側にある「void」は戻り値の型が無いことを示します。これもユーザー定義関数などでString型を返す関数であればString、int型を返すのであればintと明記する必要があります。

引数の部分も同じようにmain(String[] args)はString型の配列を引数に取り、それをargsという名前に入れるという意味で、このString[]もargsという名前の変数の型を指定しているわけです。

このように型を強く意識し、変数の型が動的に変化しない言語が静的型付け言語です。面倒に思えますが、変数に予期しない型が入ることを予防してくれる安心感もあります。

静的型付け言語と動的型付け言語の違い

静的型付け言語の対義語として動的型付け言語があります。例えば動的型付け言語のPythonでは下のようなコードが普通に許されます。

x=10
print(x)
x="Minecraft"
print(x)

# 10
# Minecraft

xという変数に数値の10を入れた後、Minecraftという文字列を入れ直しています。動的型付け言語では通って当然の処理ですが、JavaやKotlinなどの静的型付け言語では通りません。

Kotlinで同じことをしようとすると、Type mismatchというエラーになります。

fun main(args: Array<String>) {
    var num=10
    val str="Minecraft"
    num=20
    num="Minecraft"
}
//エラー:(5, 9) Kotlin: Type mismatch: inferred type is String but Int was expected

なお変数に再代入する場合はvarは不要です。実際4行目で20を再代入していますが、ここではエラーは起こりません。

しかし一度Int型として宣言されたnumという名前の変数は(基本的には)Int型しか受け付けられず、全く違う種類の型であるString型を代入しようとするとエラーが起こります。

ちなみにこのエラーを直訳してみると、「型が違う : 推論された型はStringだけど、ここで想定されているのはInt型だ」となります。

Javaでも同じようにエラーとなります。これが静的型付け言語の特徴です。

JavaとKotlinの違い - 型推論

ではJavaとKotlinの違いは何でしょうか?

上記のようにJavaでは変数宣言する場合、まず変数の型を決めてから値を代入していますが、Kotlinのコードではそれが見当たりません。

一応Kotlinでも以下のように変数や定数の型を明示的に指定することは可能です。

fun main(args: Array<String>) {
    var num :Int=10
    val game :String="Minecraft"
    println("${game}は${num}周年")
}

しかしこのような単純な変数宣言の場合、IntelliJはこの明示的な型指定をあまり良く思っていないようです。

型指定の部分にカーソルを合わせるとこんな文章が出てきます。和訳してみましょう。

明示的に型指定することは冗長(じょうちょう)である

つまり「無駄に長くなるからやめていいよ」と言っているわけです。

明示的な型指定が無駄で冗長な理由

なぜ冗長なのか。それはKotlinが型推論の機能を持った言語だからです。

上の例で変数numに代入される右辺の10はどう見ても整数です。この場合、例え値がByte型やShort型で表せる数値であってもKotlinはデフォルトでInt型を選択し、変数の型も自動的にInt型になるよう取り計らいます。

Kotlin>「10って普通の整数だよね。じゃとりあえず変数numの型はIntでいいんじゃない?」

Byte型やShort型を選ばないのは、Intの方が対応している数値の幅が広く、変数の値が後々大きな値になっても対応しやすいためですが、この「とりあえず一般的に見てこれだろう」と推論して自動的に変数の型が決まるのが型推論です。

型推論はあくまで推論

ただKotlinによる型推論は開発者側の都合で変更することが可能です。

例えば「この数値はそこまで大きな数値が出ては困る」というのであれば型を明示的に指定しましょう。この場合はIntelliJから先ほどの文句は出てきません。

「Short」とその下の「String」では色が変わっているのが分かると思います。変数numをShort型として宣言したい場合、明示的に指定するのが妥当だからです。

ちなみにShort型はマイナスも含め65536段階、Byte型は256段階まで対応している数値型です

Javaにも「一応ある」型推論

この型推論、現在ではJavaでも使うことはできます。Java10以降では型推論が導入され、以下のような記述も許されるようになりました。

var num = 10;
final var game = "Minecraft";

ただJavaは8以前の環境が未だに多いこともあり、そこまで一般的に型推論が使われているということも現時点ではありません。

「一応Javaにも型推論はある」ということだけご紹介しておきましたが、まだまだ明示的な型指定の方が支配的であると思います。

動的型付け言語は型推論?

ここでありがちなのが、「動的型付け言語は型推論をしているから型指定が不要」というのがありますが、これは全くの誤解です。

JavaScript、Python、PHPなどの動的型付け言語は型推論しているわけではなく(PHPは明示的に型指定する場合もありますが)、そもそも型のチェックなどしていません。

これらの言語は「来るもの拒まず」で何の型であってもとりあえず変数に代入できてしまうというだけです。

なのでもし、おかしな型が入っていれば実行時にエラーが出ます。静的型付け言語の場合、この実行時エラーを極力減らすためにコードを書く時点で型のチェックを行い、もしおかしな型が入ってしまっているようなコードであればコンパイル時エラーを起こします。

実行時エラーを起こさないということは大規模なプロジェクトになればなるほど大事になってきます。逆に小規模な開発の場合、いちいち型指定するのはそれこそ冗長である場合もあるでしょう。

大規模開発に向いた言語に静的型付けが多く、小規模開発に向いたスクリプト言語に動的型付けが多いというのも納得出来るのではないでしょうか。

まとめ

最後に今回のお話のポイントをまとめておきます。

  • 静的型付け言語は型を強く意識する言語
  • 静的型付け言語の変数の型は、一度決まれば自動的に変化したりしない
  • KotlinはJavaと同じ静的型付け言語である
  • Kotlinは型推論を持ち、推論できる型であれば型指定が不要
  • 動的型付け言語は型推論をしているわけではない

次回は変数についてもう少し詳しく。そしてKotlinのもう1つの大きな特徴、Null Safetyについてです。

タイトルとURLをコピーしました