前から気になっていたJavaの変性について、調べたことを自分の中で簡単にまとめて置きたくて。
プラスして、最近好きな言語であるKotlinと比較してみようかなと。
まだまだ勉強中の身であるため、間違いがあったら申し訳ありません。
不変
同じ型でなければなりません。
Java
Javaにおける不変は**List<T>**
List<String> strList = new ArrayList<String>();
List<Object> objList = strList; //コンパイルエラー 不変のため代入できない
Javaでの配列は次項の共変になります。
Kotlin
Javaとは異なり、配列がこれになります。
また、KotlinではlistOfやarrayListOf,mutableListOfでJavaにおけるリストを用意することができます。
//配列
val intArray: Array<Int> = arrayOf(1,2)
val numArray: Array<Any?> = intArray //コンパイルエラー 共変ではないため
//リスト
val strList: java.util.ArrayList<String> = arrayListOf("1st", "2nd")
因みに余談ですが、Array<Int>型を定義したが、Kotlinでは型ごとの配列も用意できるみたいです。
val intArray: IntArray = intArrayOf(3,4)
共変
広い定義に狭い定義のものを入れることが出来ます。
Java
Javaでの共変は配列と上限付き境界ワイルドカードがあります。
String型はObject型のサブクラスであるため、それぞれ以下の様に書くことが出来ます。
//配列
String[] strArray = {"hoge", "foo"};
Object[] objArray = strArray;
//上限付き境界ワイルドカード
List<String> strList = new ArrayList<String>();
List<? extends Object> objList = strList;
しかし、上記の様に全てのスーパークラスであるObject型で定義するとJavaの配列では、
objArray[0] = 1; //ランタイムエラー
このようにString型配列の中に、それ以外を入れてもコンパイルは通り、ランタイムエラーとなります。
リストの方は、String型であろうとObject型であろうとaddメソッドで加えることは出来ませんが、getメソッドでString型やObject型を取得することはできます。
(nullをaddすることはできます)
Kotlin
Kotlinでは**out修飾子**によって共変を実現しています。
そしてJavaの配列のときと異なり、出力のみ可能とすることで安全に扱うことができます。
val intArray: Array<Int> = arrayOf(1,2)
val numArray: Array<out Any?> = intArray
numArray[0] = 1 //コンパイルエラー
反変
共変の逆になります。
Java
Javaでの反変には下限付き境界ワイルドカードがあります。
Object型はString型のサブクラスを持っているので、以下の様に書くことが出来ます。
//下限付き境界ワイルドカード
List<Object> objList = new ArrayList<Object>();
List<? super String> strList = objList;
下限付きになることで、この場合String型とそのサブクラスをaddメソッドで加えることが可能となります。nullもadd出来ます。
逆にgetメソッドで取得出来るのは全てのスーパークラスであるObject型のみになります。
kotlin
Kotlinでは**in修飾子によって反変を実現しています。
共変とは逆に入力**のみ可能となっており、出力はできません。
fun main(args: Array<String>) {
var hoge = Hoge(1)
val hogeList: java.util.ArrayList<Hoge> = arrayListOf(hoge)
val fooList: java.util.ArrayList<in Foo> = hogeList //反変だから代入できる
val foo = fooList[0]
println(hoge == foo) // true
println(hoge.a) //1が出力される
println(foo.a) //コンパイルエラー
}
open class Hoge(val a: Int)
class Foo: Hoge(0)