【Java入門】型変換について（キャスト、プロモーション）

#目的

Java言語を含めたプログラミングの学習を始めたばかりの方、既学習者の方は復習用に、
型変換を学ぶ為に書いています。

前回は[変数と型](https://qiita.com/morioheisei/items/3d27e82d6cd88068dded)について学びました。
今回は**型変換**についてです。


[【Java入門目次】](https://qiita.com/morioheisei/items/6c7fc063c26e63d890ac)
・[変数と型](https://qiita.com/morioheisei/items/3d27e82d6cd88068dded)
・型変換  ←今ここ
・[変数のスコープ](https://qiita.com/morioheisei/items/c56ab32b68ce92d32206)
・[文字列の操作](https://qiita.com/morioheisei/items/e58adadd8535971b69be)
・[配列の操作](https://qiita.com/morioheisei/items/b6acbb1b0c07b40e7f56)
・[演算子](https://qiita.com/morioheisei/items/63088c61e8234dc1f7f5)
・[条件分岐](https://qiita.com/morioheisei/items/194cdc73a29bddade0a4)
・[繰り返し処理](https://qiita.com/morioheisei/items/e686e33b60c6a60e87f0)
・クラスについて（準備中）
・抽象クラス（準備中）
・インターフェース（準備中）
・カプセル化（準備中）
・モジュールについて（準備中）
・[例外処理について](https://qiita.com/morioheisei/items/d25d2b67e7530c1aab9d) 
・[ラムダ式について](https://qiita.com/morioheisei/items/2bfcc3d8d8440b00e6a9)
・[Stream APIについて](https://qiita.com/morioheisei/items/3a7be38b4d3ef712ccb2)



#型変換

データ型を変換する事です。以下がその方法になります。

・暗黙型変換
・キャストによる型変換

それぞれ見ていきましょう。

##暗黙型変換
小さな型を大きな型に代入する事は可能。
我々プログラマーが明示的に型の変換を行わなくても、コンパイルの時に型を変換してくれる。

左の型から右の型への代入はOK！
**byte → short、char → int → long → float → double**


```Java:Main.java
class Main {
  public static void main(String args[]) {
    int number = 10;

    // int型のnumberが、float型になる
    float numberFloat = number;

    System.out.println(numberFloat); // 10.0　←小数点がついた
  }
}
```

**以下は暗黙型変換が出来ませんのでご注意を。**
・char型をshort型にするとき
・short型をchar型にするとき
どちらも16ビットのデータ型ではあるが、取り扱う事のできる値の範囲が異なるためです。
以降で記載する、キャストによる型変換を行わなければいけません。

```Java:Main.java
class Main {
  public static void main(String args[]) {
    // ・ char → short
    char a = 100;
    // エラーがでる
    // short b = a; // Type mismatch: cannot convert from char to short
    
    short b = (short) a; // キャストによる型変換でshort型に変換している
    System.out.println(b); // 100

    

    // ・ short → char
    short c = 100;
    // エラーがでる
    // char d = c; // Type mismatch: cannot convert from short to char
    
    char d = (char) c; // キャストによる型変換でchar型に変換している
    System.out.println(d); // d
  }
}
```
・大きな型を小さな型に代入する事は不可能。コンパイルエラーになる。
右の型から左の型への代入はNG！
**byte → short、char → int → long → float → double**

```Java:Main.java
class Main {
  public static void main(String args[]) {
    int numberInt = 10;

    // 大きな型から小さな型への代入はできない。
    byte numberByte = numberInt; // Type mismatch: cannot convert from int to byte

    System.out.println(numberByte);
  }
}
```

また、**以下の型変換によって精度が落ちる危険性がある**ので注意してください。

```Java:Main.java
class Main {
  public static void main(String args[]) {
    // 各型の最大値をもった変数を定義
    int intMax = Integer.MAX_VALUE;
    long longMax = Long.MAX_VALUE;
    float floatMax = Float.MAX_VALUE;

    // float型の有効桁数は10進数で7桁(2進数で24桁)
    
    // int → float
    float floatFromIntMax = intMax;
    System.out.println(intMax); // 2147483647
    // ↓有効桁数7桁以降の値が失われている（型変換前と異なる結果が出力される）
    System.out.println(floatFromIntMax); // 2.14748365E9

    // long → float
    float floatFromLongMax = longMax;
    System.out.println(longMax); // 9223372036854775807
    // ↓有効桁数7桁以降の値が失われている（型変換前と異なる結果が出力される）
    System.out.println(floatFromLongMax); // 9.223372E18


    // double型の有効桁数は10進数でおよそ15桁(2進数で53桁)

    // long → double
    double doubleFromLongMax = longMax;
    System.out.println(longMax); // 9223372036854775807
    // ↓有効桁数15桁以降の値が失われている（型変換前と異なる結果が出力される）
    System.out.println(doubleFromLongMax); // 9.223372036854776E18

    // float → double
    double doubleFromFloatMax = floatMax;
    System.out.println(floatMax); // 3.4028235E38
    // ↓有効桁数15桁以降の値が失われている（型変換前と異なる結果が出力される）
    System.out.println(doubleFromFloatMax); // 3.4028234663852886E38

  }
}
```






##キャストによる型変換

大きな型から小さな型への代入はできない。
**（）**`キャスト演算子`を使用して、明示的に型変換を行う。

```Java:Main.java
class Main {
  public static void main(String args[]) {
    double numberDouble = 3.14;

    // 大きな型から小さな型への代入はできない。
    // int numberInt = numberDobule; // Type mismatch: cannot convert from double to int

    // キャスト演算子を用いて、int型に強制的に変換している
    int numberInt = (int)number; 
    System.out.println(numberInt); // 3 と出力される
  }
}
```

変換できないパターンもありますのでご注意を。
文字列をint型にはキャスト演算子を用いて変換できません。

```Java:Main.java
class Main {
  public static void main(String args[]) {
    // 変換できない例
    String str = "おはようございます";
    int num = (int)str; // Cannot cast from String to int
    System.out.println(str);
  }
}
```

**キャスト時の注意**

キャストによる型変換によって失われるデータもあるので注意してください。

・大きいサイズから小さいサイズに型変換した時、上位ビットの値が切り捨てられる（データの溢れ）

```Java:Main.java
class Main {
  public static void main(String args[]) {
    short x = 257; // 16ビット -> 0000 0001 0000 0001
    byte y = (byte)x; // 8ビット -> 0000 0001
    System.out.println(y); // 1 と出力される
  }
}
```

・少数から整数に変換する際は、小数点以下が切り捨てられる。

```Java:Main.java
class Main {
  public static void main(String args[]) {
    // また少数から整数に変換する際は、小数点以下が切り捨てられる。
    double d = 10.23;
    int i = (int)d;
    System.out.println(i); // 10 と出力される

    float f = 33.44f;
    int i2 = (int)f;
    System.out.println(i2); // 33 と出力される
  }
}
```




##プロモーション（昇格）

プログラマーの意図しない型変換をJavaが自動で行うことがあります。
以下で説明します。

###オペランド（演算対象）の一方がlong型であり他方のオペランドがfloat型、double型ではない場合、他方のオペランドはlong型に変換されてしまう

```Java:Main.java
class Main {
  public static void main(String args[]) {
    int a = 1;
    long b = 2;

    // 演算前に変数aがlong型に変換されてしまう
    int c = a + b; // Type mismatch: cannot convert from long to int
  }
}
```
↓ **解決策**

```Java:Main.java
class Main {
  public static void main(String args[]) {
    int a = 1;
    long b = 2;

    int c = (int)(a + b); // 演算結果をint型にキャストする事でエラーは消えます
    System.out.println(c); // 3
  }
}
```

###オペランド（演算対象）の一方がfloat型であり他方のオペランドがdouble型ではない場合、他方のオペランドはfloat型に変換されてしまう

```Java:Main.java
class Main {
  public static void main(String args[]) {
    int a = 1;
    float b = 2.0f;

    // 演算前に変数aがfloat型に変換されてしまう
    int c = a + b; // Type mismatch: cannot convert from float to int
  }
}
```
↓ **解決策**

```Java:Main.java
class Main {
  public static void main(String args[]) {
    int a = 1;
    float b = 2.0f;

    int c = (int)(a + b); // 演算結果をint型にキャストする事でエラーは消えます
    System.out.println(c); // 3
  }
}
```
###オペランド（演算対象）の一方がdouble型である場合、他方のオペランドはdouble型に変換されてしまう

```Java:Main.java
class Main {
  public static void main(String args[]) {
    int a = 1;
    double b = 2.0;

    // 演算前に変数aがdouble型に変換されてしまう
    int c = a + b; // Type mismatch: cannot convert from double to int
  }
}
```
↓ **解決策**

```Java:Main.java
class Main {
  public static void main(String args[]) {
    int a = 1;
    double b = 2.0;

    int c = (int)(a + b); // 演算結果をint型にキャストする事でエラーは消えます
    System.out.println(c); // 3
  }
}
```

###オペランド（演算対象）がlong型、float型、double型のいずれでもない場合、双方のオペランドはint型に変換されてしまう

```Java:Main.java
class Main {
  public static void main(String args[]) {
    byte a = 1;
    byte b = 2;

    // 演算前に変数a,bがint型に変換されてしまう
    byte c = a + b; // Type mismatch: cannot convert from int to byte
  }
}
```
↓ **解決策**

```Java:Main.java
class Main {
  public static void main(String args[]) {
    byte a = 1;
    byte b = 2;

    byte c = (byte)(a + b); // 演算結果をbyte型にキャストする事でエラーは消えます
    System.out.println(c); // 3
  }
}
```


プロモーション（昇格）によって意図しない結果になり、エラーが出てしまう事があると思います。
計算時の数値昇格は意識して気をつけなければいけませんね。

#終わりに

型変換について学びました。
次回は[変数のスコープ](https://qiita.com/morioheisei/items/c56ab32b68ce92d32206)を掘り下げていこうと思います。