前から気になっていた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)