Java でのデータ型の理解

著者は、フリーかつオープンソースファンドを、寄付の一環としての寄稿プログラムの一環として選択しました。

紹介

Java静的型付けのプログラミング言語です。これは、変数を作成するときにそのデータ型も指定する必要があることを意味します。これは動的型付けの言語とは対照的であり、そのような言語にはPHPが含まれます。動的型付けの言語では、変数のデータ型を指定する必要はなく、それは安心感のように感じられるかもしれません。

しかし、データ型を知り、適切に使用することで、開発者はコードを最適化することができます。なぜなら、各データ型には特定のリソース要件があるからです。また、1つのデータ型を指定して異なる型を保存しようとした場合、間違ってしまう可能性があるため、コンパイルできなくなります。したがって、静的型付けの言語では、テスト前でもエラーを検出することができます。

Javaには2つのデータ型があります:プリミティブ型と参照型(または非プリミティブ型とも呼ばれます)。このチュートリアルでは、Javaプログラムで情報を保存および使用するために変数を使用し、Javaでよく使用されるいくつかのデータ型について学びます。これはすべてのデータ型の包括的な概要ではありませんが、このガイドを使ってJavaで利用可能なオプションに慣れることができます。

前提条件

このチュートリアルを進めるためには、以下が必要です:

  • 例に従ってJavaプログラムを実行できる環境。ローカルマシンでこれをセットアップするには、次のものが必要です:

    • マシンにインストールされたJava(バージョン11以上)と、Java Development Kit(JDK)によって提供されるコンパイラが必要です。UbuntuとDebianの場合は、当社のチュートリアル「Ubuntu 22.04にAptを使用してJavaをインストールする方法」のオプション1の手順に従ってください。MacやWindowsを含む他のオペレーティングシステムの場合は、Javaインストールのダウンロードオプションをご覧ください。
    • コード例のコンパイルと実行には、このチュートリアルではJavaシェルが使用されています。これは、コマンドラインから実行されるRead-Evaluate-Print Loop(REPL)です。JShellを始めるには、「JShellの紹介」ガイドをご覧ください。
  • Javaおよびオブジェクト指向プログラミングに精通していることが必要です。これについては、当社のチュートリアルで確認できます。Javaで最初のプログラムを書く方法

プリミティブタイプ

Javaのプリミティブタイプは、Javaで最も単純で基本的なデータ型です。数値や文字などの生の値を表します。最もよく使用されるプリミティブデータ型には、int(整数)、boolean(ブール値)、およびchar(文字)があります。残りのデータ型は公式のJavaデータ型のドキュメントで見つけることができます。

整数

整数は負の数と正の整数の両方です。Javaでは、それらを格納するためにintを使用します。 intは、ほとんどの目的に十分な大きさの数値を収容できます: -2,147,483,648から2,147,483,647まで。

intの使用例を見てみましょう:

int theAnswer = 42;

プリミティブタイプは常に小文字で始まります(int)。 Javaの構文ルールでは、まずデータ型(int)を指定し、その後に変数名(theAnswer)を指定する必要があります。その後、等号(=)で値42を変数に割り当てます。

データ型に関係なく、特殊な文字を前に付けずに変数名を直接指定して使用します。これはJavaが変数として認識できるためです。

注意:このチュートリアルの変数theAnswerおよび他のすべての変数の名前は、キャメルケースで記述されています。厳格な要件はありませんが、これはJavaで受け入れられている命名規則です。

変数を宣言した後、次のようにメソッド内で参照して使用することができます:

int theAnswer = 42;
System.out.println("The answer to all questions is " + theAnswer);

2行目では、System.outパッケージの組み込みメソッドprintlnを使用してコンソールにtheAnswerを表示します。これは変数が期待どおりに宣言されているかをテストするための最も簡単な方法です。

このコードを実行するには、Javaシェルツールを使用します。Javaをインストールした後、ローカルコンピューターでターミナルまたはコマンドプロンプトを開き、jshellと入力します:

  1. jshell

出力は次のようになります:

Output
| Welcome to JShell -- Version 11.0.16 | For an introduction type: /help intro jshell>

このチュートリアルのコード例をコンソールに貼り付けることができます。終了する場合は、jshellと入力して終了します:/exit

intを宣言して使用するには、次の行をjshellコンソールに貼り付けます:

  1. int theAnswer = 42;
  2. System.out.println("The answer to all questions is " + theAnswer);

次のような出力が表示されます:

Output
theAnswer ==> 42 The answer to all questions is 42

この出力は、int型の変数theAnswerが正しく42に設定されていること(theAnswer ==> 42)を確認します。また、theAnswerをメソッドに渡して使用し、メソッドが期待する変数の値を生成しました。

ブール

ブール値はtrueまたはfalseです。Javaでは、それらを保存するためにbooleanを使用します。たとえば、Javaが楽しいかどうかを定義するboolean変数を作成しましょう:

boolean isJavaFun = true;

変数isJavaFuntrueとして定義します。代替のboolean値はfalseです。

上記の変数を使用して、次のように文Java is fun: trueを出力できます:

  1. boolean isJavaFun = true;
  2. System.out.println("Java is fun: " + isJavaFun);

jshellでこれらの行を実行すると、次の出力が生成されます:

Output
isJavaFun ==> true Java is fun: true

intの例と同様に、printlnメソッドは括弧内で提供された引数を出力します。プラス記号(+)は文字列「Java is fun:」を変数isJavaFunと連結または結合するために使用されるので、実際には1つの引数、すなわち文字列Java is fun: trueです。

文字

単一の英数字文字を保存するには、charを使用します。たとえば:

char firstLetter = 'a';

文字aが単一引用符で囲まれていることに注意してください。単一引用符はchar値にのみ使用できます。ダブルクォーテーションは、後で学習するように、文字列に使用されます。

は、1文字に変数を割り当てる必要がないため、特に有用なタイプではないようです。ただし、は、などの文字列クラスの構築ブロックとして使用されます。これらは基本的に値のコレクションです。

このセクションで見たように、プリミティブ型変数の宣言と使用は、整数などの単純な値を表すため、簡単です。これらの値は使用する準備ができており、オブジェクトの作成、メソッドの呼び出し、などの追加の操作は必要ありません。

参照型

このシリーズの最初のチュートリアルであるJavaで最初のプログラムを書く方法では、Javaコードがクラスに組織され、これらのクラスがオブジェクトを作成するテンプレートとして使用されることを学びました。そのようなオブジェクトが変数に割り当てられると、これらのオブジェクトを指し示すか参照しています。これらの場合、変数は参照型として分類されます。これらの変数は非プリミティブとも呼ばれますが、プリミティブ型の変数はオブジェクトを指すことはできません。

オブジェクトは、高度なプロパティを持ち、メソッドをトリガーすると動作する能力があるため、強力です。しかし、それらにポイントする変数がないと、これらのオブジェクトはアクセスできず、実際には使用できません。これが、参照型変数がJavaとオブジェクト指向プログラミング全体に不可欠である理由です。

注意:参照型はクラスから作成されたオブジェクトを指します。混乱を避けるため、次の例では参照型と作成されたオブジェクトは同じクラスになります。

しかし、複雑なプログラムでは、これがまれです。Javaでは、インターフェイスは特定の動作の要件のグループであり、これらの要件は1つ以上のクラスで満たすことができます。インターフェイスの要件を満たすクラスは、このインターフェイスを実装したと言います。したがって、複雑なプログラムでは、インターフェイスの参照型の変数を宣言することが一般的です。これにより、変数が具体的な動作の実装に結び付けられることなく、変数が示すべき動作を指定できます。これにより、変数が使用される方法を変更せずに、変数が指す実装を簡単に変更できます。この複雑な概念は、Javaシリーズの別のチュートリアルで行われる継承および多様性に関するより高度なトピックの一部です。

ある程度の原始的な型しかない一方で、参照型は事実上無制限です。なぜなら、クラス(およびインターフェース)の数に制限がなく、各クラスが参照型を表しているからです。Javaには基本的な機能を提供する多くの組み込みクラスがあります。最も頻繁に使用されるものは、コアのjava.langパッケージにあります。このセクションでは、その中からいくつかを見ていきます。

Stringクラス

Stringクラスは、文字の組み合わせを表します。Stringまたは他の参照型変数を宣言するには、まずその型を指定してから名前を指定します。その後、等号を使用して値を割り当てます。これまでは原始的な型と同じような操作です。ただし、参照型はオブジェクトを指しますので、まだオブジェクトが作成されていない場合はオブジェクトを作成する必要があります。以下は例です:

String hello = new String("Hello");

helloは、参照型Stringを持つ変数の名前です。これを新しいStringオブジェクトに割り当てます。新しいStringオブジェクトは、newキーワードとクラスの名前(この場合はString)を使用して作成されます。クラスStringは大文字で始まります。慣例として、すべてのクラスおよびそれに伴う参照型は大文字で始まります。

各クラスには、新しいオブジェクトを作成するために使用される特別なメソッドがあります。constructorクラス名の末尾に括弧(())を追加することで、このコンストラクタを呼び出すことができます。コンストラクタはパラメーターを受け取る場合があります。例えば、"Hello"パラメーターがStringのコンストラクタに適用される場合です。

hello変数が期待どおりに動作することを確認するには、printlnメソッドに再度渡します。次のようにして:

  1. String hello = new String("Hello");
  2. System.out.println(hello);

jshellでこれらの行を実行すると、次の出力が生成されます:

Output
hello ==> "Hello" Hello

今回は、出力がhello変数がHelloに設定されていることを確認します。その後、同じHelloが新しい行に印刷され、println()メソッドがそれを処理したことが確認されます。

Wrapper Classes

前のセクションでは、頻繁に使用されるString参照型を扱いました。他の人気のある参照型は、プリミティブ型のラッパーと呼ばれるものです。ラッパークラスは、プリミティブデータをラップまたは含んでいるため、その名前があります。すべてのプリミティブ型にはラッパーの対応があり、ここにいくつかの例があります:wrapper class

  • Integer: int値をラップするために使用されます。
  • Character: char値をラップするために使用されます。
  • Boolean: boolean値をラップするために使用されます。

これらのラッパーは、単純なプリミティブ値を強力なオブジェクトにアップグレードするために存在しています。各ラッパーには、それが設計された値に関連する使用準備ができたメソッドがあります。

例として、Integerを探索します。前のセクションでは、newキーワードを使用してStringオブジェクトを作成しました。ただし、一部のクラスでは、オブジェクトを取得するための特殊なメソッドの使用が提供されており、さらに推奨されています。そして、Integerもその1つです。 Integerの場合、特殊なメソッドの使用は主にリソースの最適化に関するものですが、他の場合では複雑なオブジェクトの構築を簡素化するためのものです。

次の例では、valueOfメソッドを使用して、値42を持つInteger変数theAnswerを作成します。

  1. Integer theAnswer = Integer.valueOf(42);
  2. System.out.println(theAnswer);

jshellで、以下の出力が表示されます:

Output
theAnswer ==> 42 42

IntegerメソッドvalueOf(42)を呼び出すことで、Javaにこの値を持つオブジェクトを取得するように指示します。裏側では、Javaは既にその値を持つオブジェクトがキャッシュに存在するかどうかをチェックします。存在する場合、オブジェクトはtheAnswer変数にリンクされます。存在しない場合、新しいオブジェクトがtheAnswer変数のために作成されます。

パフォーマンスのために、多くの組み込みクラスがこのようなメソッドを提供しており、使用が推奨されています(必須ではありません)。Integerの場合は、まだnewキーワードを使用してオブジェクトを作成することができますが、非推奨の警告が表示されます。

さらに、Stringとラッパーに加えて、java.langパッケージの概要で見つけることができる他の便利な組み込み参照型もあります。これらのより高度な参照型を完全に理解するには、追加の説明や事前知識が必要です。そのため、Javaシリーズの次のチュートリアルでいくつかをカバーします。

リテラル

リテラルは、コード内で直接使用できる固定値を表します。そのため、プリミティブ型と参照型の両方に割り当てることができます。いくつかの種類のリテラルがあり、以下のように分類することができます。

プリミティブ型のリテラル

すでにプリミティブ型のセクションでいくつかのリテラルを使用しています。各プリミティブ型には、例えば、42'a'trueのようなリテラルがあります。 42のような整数は整数リテラルです。同様に、'a'のような文字は文字リテラルであり、truefalseはブールリテラルです。

プリミティブ型のリテラルは、参照型の値を作成するためにも使用することができます。 int リテラルは、Integer.valueOf(42) のコードで Integer オブジェクトを作成するために使用されました。これには短縮形もあり、次のように値を直接割り当てることもできます:

Integer theAnswer = 42;

42 は整数リテラルであり、任意の整数のように、追加の文なしで theAnswer 変数に直接割り当てることができます。これは便利なので、Integer の宣言はよくこのように見られます。

この短縮記法は、他のプリミティブ型リテラルとそれに対応する参照型にも適用されます。たとえば、Boolean の場合は次のようになります:

Boolean isFun = true;

true はリテラルであり、Boolean 型の isFun 変数に直接割り当てることができます。同様に、false リテラルも同じ方法で割り当てることができます。

文字列リテラル

String 参照型には特別なリテラルもあり、その値を囲む二重引用符で認識されます。この例では、"Hello, World!" です:

String helloWorld = "Hello, World!";

リテラルを使用すると、よりシンプルで短くなりますので、多くのプログラマーがそれを好んで使用します。ただし、参照型のセクションで既に行ったように、新しい String オブジェクトを使用して String 変数を宣言することもできます。

Nullリテラル

もう1つ重要なリテラルがあります:null、これは値の欠如またはオブジェクトの非存在を表します。Nullは、オブジェクトを指す代わりにnullを指す参照型を作成することを可能にします。nullはすべての参照型に使用できますが、任意のプリミティブ型には使用できません。

nullリテラルには1つの注意点があります:変数をそれで宣言できますが、適切な非null値を再代入するまでこれらの変数を使用できません。 null値を持つ参照型変数を使用しようとすると、エラーが発生します。 以下に例を示します:

  1. String initiallyNullString = null;
  2. System.out.println("The class name is: " + initiallyNullString.getClass());

jshellでこのコードを実行しようとすると、次のようなエラーが表示されます:

Output
initiallyNullString ==> null | Exception java.lang.NullPointerException | at (#4:1)

使用しているオペレーティングシステムとJavaバージョンによって、出力が異なる場合があります。

エラーjava.lang.NullPointerExceptionがスローされます。これは、変数initiallyNullString(nullオブジェクトを指す)でStringメソッドgetClass()(クラス名を返す)を呼び出そうとしているためです。

注意: 簡単のため、java.lang.NullPointerExceptionをエラーと呼んでいますが、厳密には例外です。 例外とエラーについて詳しくは、チュートリアル「Javaの例外処理」を参照してください。

エラーに対処するには、initiallyNullString の値を次のように再割り当てする必要があります:

  1. String initiallyNullString = null;
  2. initiallyNullString = "not null any longer";
  3. System.out.println("The class name is: " + initiallyNullString.getClass());

修正された新しいコードは、以下の出力を表示します:

Output
initiallyNullString ==> null initiallyNullString ==> "not null any longer" The class name is: class java.lang.String

上記の出力は、initiallyNullString が最初にnullであり、その後"not null any longer"を含む新しいStringオブジェクトになる様子を示しています。次に、インスタンス化されたオブジェクトに対してgetClass()メソッドが呼び出されると、java.lang.Stringが取得されます。ここで、Stringはクラス名であり、java.langはそのパッケージです。最後に、完全で意味のあるメッセージが表示されます:"The class name is: class java.lang.String"

このようなnull値の宣言は、古いコードではより一般的です。通常、後で実際の値を割り当てるために使用され、通常は後者を決定するロジックを経ています。ただし、Javaバージョン8以降では、Optionalと呼ばれる新しい参照型があり、通常はnullが使用されていた場合に適しています。

ローカル変数型推論

ここまで、Javaで変数を定義するために一般的なデータ型を使用してきました。しかし、Java 10では、ローカル変数型推論と呼ばれる新機能が導入されました。これにより、新しい変数の前にキーワードvarを使用できます。この機能により、Javaはローカルコンテキストからデータ型を(つまり、自動的に推測して)推論します。型推論は、以前に説明した変数の定義の冗長性と対照的であるため、議論の的です。このような機能の利点と欠点は議論の余地がありますが、他の静的型付け言語、例えばC++は型推論をサポートしています。

いずれにせよ、型推論はローカル変数、つまりメソッド内の変数にのみ適用されるため、データ型の使用を完全に置き換えることはできません。varを使用した例を見てみましょう:

  1. var hello = "Hello";
  2. System.out.println(hello);

helloという変数をvarキーワードで宣言して、Javaにそのデータ型を検出するよう指示します。その後、通常通りにコンソールに出力して動作を確認します:

Ouput
hello ==> "Hello" Hello

この例は、Javaのインストール(具体的にはJDK)がバージョン10より上である限り動作します。varキーワードは、古いバージョンではサポートされていません。

型推論はコンパイルプロセス中に行われます。つまり、コードをコンパイルする際に行われます。コンパイルプロセスは、プレーンテキストのソースコードをマシンコードに変換し、型推論を含むさまざまな最適化を適用します。これにより、型推論された変数に必要な正確なシステムメモリ量が利用可能になります。したがって、コンパイル後に実行するマシンコードは、すべてのデータ型を手動で指定したかのように完全に最適化されます。

この例では、変数がローカルであるため、varキーワードが機能します。また、varデータ型はローカル変数のみで機能します。ローカル変数はメソッド内で定義され、メソッド内でのみアクセス可能です。そのため、「ローカル」と呼ばれています。

ローカル変数以外ではvarを使用できないことを示すために、次のようにmainメソッドの外に配置してみてください:

  1. public class Hello {
  2. var hello = "Hello";
  3. public static void main(String[] args) {
  4. // example code
  5. }
  6. }

上記のコードをjshellに貼り付けると、次のエラーが表示されます:

Output
| Error: | 'var' is not allowed here | var hello = "Hello"; | ^-^

helloがメソッドの外にあるため、varはそこでは許可されません。したがって、非ローカル変数では型推論は機能せず、コンテキストを信頼してデータ型を検出することができません。

varを使用することは難しい場合があり、必須ではありませんが、遭遇することがあるため、知っておくと役立ちます。

予約キーワード

変数を宣言する際、Javaには知っておくべきもう1つの重要なルールがあります。変数名には使用できない予約キーワードがあります。たとえば、int型のプリミティブをnewという名前で宣言することはできません:

  1. int new = 1;

この例を試すと、newが予約キーワードであるため、コンパイルエラーが発生します。

Output
| Error: | '.class' expected | int new = 1; | ^ | Error: | <identifier> expected | int new = 1; | ^ | Error: | '(' or '[' expected | int new = 1; | ^ | Error: | unexpected type | required: value | found: class | int new = 1; | ^--^ | Error: | missing return statement | int new = 1; | ^----------^

エラーのリストで最初の部分が最も重要です:

Output
| Error: | '.class' expected | int new = 1; | ^

'.class' expectedというエラーは、newキーワードを使用するときに、Javaがクラスが続くことを期待していることを意味します。この時点では、Javaは文を解釈できず、残りのエラーが続きます。

他の予約キーワード(abstractcontinuedefaultfor、およびbreakなど)もJavaで特定の意味を持ち、変数名として使用できません。予約キーワードの完全なリストはJava言語のキーワードページで確認できます。予約キーワードを全て覚えていなくても、コンパイルエラーを使用して問題を特定できます。

結論

このチュートリアルでは、Javaのプリミティブおよび参照データ型について学びました。これは複雑ですが重要なトピックです。じっくり練習し、例を何度も確認してください。データ型や値を変更してみてください。エラーが発生するタイミングとしないタイミングに注意して、成功するコードの実行に対する感覚を開発してください。

Javaに関する詳細は、当社のHow To Code in Javaシリーズをチェックしてください。

Source:
https://www.digitalocean.com/community/tutorials/understanding-data-types-in-java