静的型付けの世界
🐱 この章の目次
型はコンパイル時に確定する
Python の型ヒントはあくまでオプションであり、mypy という外部ツールで検査します。
JVM 言語では 静的型付け が必須であり、型の不整合はコンパイル時にエラーになります。
つまり、実行する前に型の問題がすべて検出されます。
# Python: 型ヒントは任意、実行時には無視される
def greet(name: str) -> str:
return f"Hello, {name}"
greet(123) # mypy はエラーにするが、実行はできてしまう
// Java: 型が合わないとコンパイルエラー
public String greet(String name) {
return "Hello, " + name;
}
greet(123); // コンパイルエラー: int を String に変換できない
// Kotlin: 型推論でより簡潔に書ける
fun greet(name: String): String = "Hello, $name"
ジェネリクスと型消去
ジェネリクス は Python の list[str] に相当する仕組みです。
ただし JVM では 型消去(type erasure) により、実行時にはジェネリクスの型情報が失われます。
これは Python の List[str] が実行時にただの list であるのと似た制約です。
// Kotlin: コンパイル時には型安全
val names: List<String> = listOf("Alice", "Bob")
// names.add(123) // コンパイルエラー
Null 安全
Python では Optional[str] で None の可能性を示しますが、実行時チェックは開発者の責任です。
Kotlin は Null 安全 を型システムに組み込み、String?(nullable)と String(non-null)を区別します。
null 参照によるクラッシュをコンパイル時に防げるのは、Python にはない大きな利点です。
// Kotlin: null を許容する型と許容しない型
fun length(s: String): Int = s.length // null を渡せない
fun safeLength(s: String?): Int = s?.length ?: 0 // null 安全な呼び出し
# Python: Optional はあくまでヒント
from typing import Optional
def safe_length(s: Optional[str]) -> int:
return len(s) if s is not None else 0
Interface と Protocol
Python の Protocol は構造的部分型(structural subtyping)を提供します。
JVM の interface は名前的部分型(nominal subtyping)であり、クラスが明示的に実装を宣言する必要があります。
この違いにより、JVM では依存関係がコンパイル時に明確になります。
// Kotlin: interface を明示的に実装
interface Greeter {
fun greet(name: String): String
}
class HelloGreeter : Greeter {
override fun greet(name: String) = "Hello, $name"
}
# Python: Protocol は構造的に一致すれば OK
from typing import Protocol
class Greeter(Protocol):
def greet(self, name: str) -> str: ...
class HelloGreeter: # 明示的な継承不要
def greet(self, name: str) -> str:
return f"Hello, {name}"