LoginSignup
35
24

More than 5 years have passed since last update.

JavaとKotlinにおける変性

Posted at

前から気になっていたJavaの変性について、調べたことを自分の中で簡単にまとめて置きたくて。
プラスして、最近好きな言語であるKotlinと比較してみようかなと。
まだまだ勉強中の身であるため、間違いがあったら申し訳ありません。

不変

同じ型でなければなりません。

Java

Javaにおける不変はList<T>

List<String> strList = new ArrayList<String>();
List<Object> objList = strList; //コンパイルエラー  不変のため代入できない

Javaでの配列は次項の共変になります。

Kotlin

Javaとは異なり、配列がこれになります。
また、KotlinではlistOfarrayListOf,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型を取得することはできます。
(nulladdすることはできます)

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メソッドで加えることが可能となります。nulladd出来ます。
逆に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)
35
24
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
35
24