Javaにおけるデータ型の理解
著者は「Write for Donations」プログラムの一環として、フリー・オープンソース・ファンドへの寄付を受けるために選ばれました。
はじめに
Javaは静的型付けのプログラミング言語です。つまり、変数を作成する際に、そのデータ型を指定する必要があります。そして、そのデータ型は、変数が保持する情報の種類です。これは、PHPなどの動的型付け言語とは対照的です。動的型付け言語では、変数のデータ型を指定する必要はありません。これは、一見すると楽に感じられるかもしれません。
しかし、データの種類を知って適切に使用することで、開発者はコードの最適化を行うことができます。なぜなら、各データの種類は特定のリソース要件を持っているからです。また、あるデータの種類を指定し、間違って別の種類を格納しようとする場合、コードをコンパイルできなくなります。そのため、静的型付け言語では、テストの前にエラーを検出することができます。
Javaには、プリミティブデータ型と参照型(非プリミティブもしくはオブジェクト型とも呼ばれる)の2つのデータ型があります。このチュートリアルでは、Javaプログラムで情報を保存し使用するために変数を使用し、Javaでよく使用されるいくつかのデータ型について学びます。これは全てのデータ型の徹底的な概要ではありませんが、このガイドはJavaで利用可能なオプションについて慣れるのに役立ちます。
前提条件
このチュートリアルを進めるためには、以下が必要です:
- An environment in which you can execute Java programs to follow along with the examples. To set this up on your local machine, you will need the following:Java (version 11 or above) installed on your machine, with the compiler provided by the Java Development Kit (JDK). For Ubuntu and Debian, follow the steps for Option 1 in our tutorial, How To Install Java with Apt on Ubuntu 22.04. For other operating systems, including Mac and Windows, see the download options for Java installation.
To compile and run the code examples, this tutorial uses Java Shell, a Read-Evaluate-Print Loop (REPL) run from the command line. To get started with JShell, check out the Introduction to JShell guide. - Familiarity with Java and object-oriented programming, which you can find in our tutorial, How To Write Your First Program in 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が変数として認識できるためです。
Note
変数を宣言した後は、このようにメソッド内で参照することによって利用することができます。
int theAnswer = 42;
System.out.println("The answer to all questions is " + theAnswer);
第2行目では、パッケージSystem.outの組み込みメソッドprintlnを使用して、theAnswerをコンソールに表示します。変数が期待どおりに宣言されているかを確認するために、これが最も簡単な方法です。
このコードが実行される様子を見るには、Java Shellツールを使用してください。Javaをインストールした後、ローカルコンピュータでターミナルまたはコマンドプロンプトを開き、jshellと入力してください。
- jshell
下記のような出力結果が表示されます。
| Welcome to JShell — Version 11.0.16 | For an introduction type: /help intro jshell>
このチュートリアルからコードの例をコンソールに貼り付けることができます。完了したら、/exitと入力してjshellを終了させることができます。
int型を宣言して使用するには、次の行をjshellコンソールに貼り付けてください。
- int theAnswer = 42;
- System.out.println(“The answer to all questions is “ + theAnswer);
以下の出力を見ることができます。
theAnswer ==> 42 The answer to all questions is 42
この出力は、int型の変数theAnswerが適切に42に設定されていること(theAnswer ==> 42)を確認しています。また、theAnswerをメソッドに渡して使用し、そのメソッドが期待される変数の値を生成しました。
ブール値
ブーリアン値は真または偽です。Javaでは、booleanを使用してそれらを保存します。例えば、Javaが楽しいかどうかを定義するブーリアン変数を作成しましょう。
boolean isJavaFun = true;
変数isJavaFunをtrueと定義します。別の真偽値のオプションはfalseです。
上記の変数を使用すると、次のようにJavaは楽しいです: trueという文を印刷することができます。
- boolean isJavaFun = true;
- System.out.println(“Java is fun: “ + isJavaFun);
jshellでこれらの行を実行すると、以下の出力が生成されます。 (Jshellで走らせたら以下の結果が出ます。)
isJavaFun ==> true Java is fun: true
intの例と同様に、メソッドprintlnはカッコ内で与えられた引数を出力します。プラス記号(+)は文字列「Java is fun: 」と変数isJavaFunを連結または結合するために使用されます。その結果、実際には1つの引数、すなわち文字列「Java is fun: true」となります。
キャラクター
単一の英数字の文字を保存するためには、charを使用します。例えば:
char firstLetter = 'a';
‘a’という文字がシングルクォートで囲まれていることに注目してください。シングルクォートは文字の値にのみ使用されます。ダブルクォートは後で学ぶように、文字列に使用されます。
charは特に有用なタイプではないようです。なぜなら、単一の文字に割り当てられた変数が必要になることはあまりないからです。しかしながら、charはStringのような文字列のクラスのための基本要素として使われています。そのため、charは基本的にchar値の集まりです。
このセクションでご覧いただいた通り、プリミティブ型変数の宣言と使用はシンプルです。なぜなら、整数のような単純な値を表しているからです。これらの値はすぐに使用することができ、オブジェクトの作成やメソッドの呼び出しなどの追加の操作は不要です。
参照タイプ
このシリーズの最初のチュートリアルである「Javaで最初のプログラムを書く方法」では、Javaコードはクラスに整理され、これらのクラスはオブジェクトを作成するためのテンプレートとして使用されることを学びました。このようなオブジェクトが変数に割り当てられると、それらのオブジェクトを指し示すか参照することになります。これらの場合、変数は参照型として分類されます。これらの変数は非プリミティブとも呼ばれますが、プリミティブ型の変数はオブジェクトを指し示すことはできません。
オブジェクトは高度な属性を持ち、メソッドがトリガーされた時に動作するため、非常に強力です。ただし、これらのオブジェクトは変数が指していない限り、アクセスできず実質的には使用できません。そのため、参照型変数はJavaとオブジェクト指向プログラミング全体にとって不可欠です。
Note
しかし、複雑なプログラムでは、これはめったに起こりません。Javaでは、インターフェースは特定の振る舞いに対する要件のグループです。そして、これらの要件は1つ以上のクラスによって満たされることができます。インターフェースの要件を満たすクラスは、そのインターフェースを実装していると言われます。したがって、複雑なプログラムでは、インターフェースのリファレンスタイプの変数を宣言するのが一般的です。これにより、変数が具体的な振る舞いの実装に結びつけられることなく、変数が示すべき振る舞いを指定することができます。変数が指す実装を簡単に変更できるため、変数の使用方法を変更することなく実装を変更できます。この複雑な概念は、継承とポリモーフィズムについてのより高度なトピックの一部であり、私たちのJavaシリーズの別のチュートリアルで説明します。
構成要素の数は少ないが、参照型は実質的に無制限であるため、クラス(およびインターフェース)の数には制限がなく、それぞれのクラスが参照型を表す。Javaには、重要な機能を提供する多くの組み込みクラスがある。最も頻繁に使用されるものは、core java.langパッケージに含まれている。このセクションでは、それらのうちいくつかを確認します。
Stringクラス
Stringクラスは、文字列を構成する文字の組み合わせを表します。String、または他の参照型変数を宣言する際には、まずその型を指定し、その後に名前を指定します。その後、等号を使って値を割り当てます。これまでは、プリミティブ型と同様です。ただし、参照型はオブジェクトを指すため、まだ作成されていない場合はオブジェクトを作成する必要があります。以下に例を示します。
String hello = new String("Hello");
こんにちは、変数の名前はString型の参照です。新しいStringオブジェクトにそれを割り当てます。新しいStringオブジェクトは、クラスの名前である「String」とともに、newキーワードを使用して作成されます。クラスStringは大文字で始まります。慣例として、すべてのクラス、つまり参照型は大文字で始まります。
各クラスには、新しいオブジェクトを作成するために使用される特別なメソッドであるコンストラクタがあります。クラス名の末尾に括弧(())を追加することで、このコンストラクタを呼び出すことができます。コンストラクタはパラメータを受け入れる場合があります。例えば、Stringのコンストラクタにはパラメーターとして「Hello」が適用されます。
こんにちは変数が正しく動作しているかを確認するために、printlnメソッドにhello変数を再度渡してください。
- String hello = new String(“Hello”);
- System.out.println(hello);
これらのコードをjshellで実行すると、次のような出力が得られます。
hello ==> “Hello” Hello
今回は、出力結果によって変数helloがHelloに設定されていることが確認されます。その後、同じHelloが新しい行に出力され、println()メソッドが処理されたことが確認されます。
ラッパークラス
前のセクションでは、よく使用される String 参照型で作業しました。他の人気のある参照型は、いわゆるプリミティブ型のラッパーです。ラッパークラスはプリミティブデータを包んで保持しているため、その名前があります。すべてのプリミティブ型には、ラッパークラスが存在し、以下にいくつかの例を示します。
- Integer: To wrap int values.
- Character: To wrap char values.
- Boolean: To wrap boolean values.
これらのラッパーは、単純なプリミティブな値を強力なオブジェクトにアップグレードするために存在しています。それぞれのラッパーには、それが設計された値に関連する使用準備が整ったメソッドがあります。
例えば、Integerを探求します。前のセクションでは、新しいキーワードを使ってStringオブジェクトを作成しました。しかし、一部のクラスでは、それらからオブジェクトを取得するための特殊なメソッドの使用が提供され、さらに奨励されることもあります。Integerもその一つです。Integerの場合、特殊なメソッドの使用は主にリソースの最適化に関するものですが、他の場合では複雑なオブジェクトの構築を簡素化するためのものとなります。
以下の例では、valueOfメソッドを使用して値が42のInteger変数であるtheAnswerを作成します。
- Integer theAnswer = Integer.valueOf(42);
- System.out.println(theAnswer);
JShellでは、次の出力が得られます。
theAnswer ==> 42 42
IntegerメソッドのvalueOf(42)を呼び出すことで、Javaに対してこの値を持つオブジェクトを取得するよう指示します。Javaは裏で、既にキャッシュにその値を持つオブジェクトがあるかどうかを確認します。もし存在する場合、そのオブジェクトはtheAnswer変数に関連付けられます。存在しない場合、新しいオブジェクトがtheAnswer変数のために作成されます。
多くの組み込みクラスは、パフォーマンスのためにそのようなメソッドを提供しており、推奨されるか、あるいは必須である場合もあります。Integerの場合、newキーワードを使用してオブジェクトを作成することはできますが、非推奨の警告が表示されます。
Stringとラッパーに加えて、java.langパッケージの概要には他にも便利な組み込みの参照型があります。これらのより高度な参照型を完全に理解するには、追加の説明や予備知識が必要です。そのため、私たちは次のJavaシリーズのチュートリアルでいくつかの参照型について説明します。
文字通り
リテラルは、コード内で直接使用できる固定値を表します。そのため、これらはプリミティブ型やリファレンス型の両方に割り当てることができます。いくつかの種類のリテラルがありますが、以下のように分類することができます。
プリミティブ型のリテラル
原始型のセクションで既にいくつかのリテラルを使用しています。各原始型には、例えば私たちの例の中の42、’a’、そしてtrueのようなリテラルがあります。42のような整数は整数のリテラルです。同様に、’a’のような文字は文字のリテラルであり、trueとfalseはブールのリテラルです。
基本型のリテラルは、参照型の値を作成するためにも使用することができます。インテジャー型のリテラルは、コードInteger.valueOf(42)でインテジャーオブジェクトを作成するために使用されました。これには略記法もあり、以下のように値を直接代入することもできます。
Integer theAnswer = 42;
42は整数リテラルであり、他の整数と同様に、追加の文なしでtheAnswer変数に直接割り当てることができます。便利なので、このように整数を宣言するのは一般的です。
この省略アプローチは、Booleanなどの他のプリミティブ型のリテラルやそれに対応する参照型にも適用することができます。
Boolean isFun = true;
真(しん)はリテラルであり、それはisFunという型がブール型(Boolean)の変数に直接割り当てられます。同様に、同じ方法で偽(ぎ)のリテラルも割り当てることができます。
文字列リテラル
String参照型には特別なリテラルも存在し、その値を囲む二重引用符で認識されます。この例では、「こんにちは、世界!」となります。
String helloWorld = "Hello, World!";
リテラルを使用する方がシンプルで短く、そのため多くのプログラマーがそれを選びます。しかし、参照型のセクションで既に行ったように、新しいStringオブジェクトを使用してString変数を宣言することもできます。
ヌルリテラル
もう一つ重要なリテラルがあります。それはnullで、値の不在やオブジェクトの非存在を表します。nullを使用することで、オブジェクトではなくnullに参照タイプを作成し、それにポイントすることができます。nullはすべての参照タイプに使用できますが、どのプリミティブタイプにも使用することはできません。
nullリテラルには1つの注意点があります:変数を宣言することはできますが、適切な非null値を再代入するまでは、これらの変数を使用することはできません。null値を持つ参照型変数を使用しようとすると、エラーが発生します。以下に例を示します:
- String initiallyNullString = null;
- System.out.println(“The class name is: “ + initiallyNullString.getClass());
このコードをjshellで実行しようとすると、以下のようなエラーが表示されます。
initiallyNullString ==> null | Exception java.lang.NullPointerException | at (#4:1)
あなたのオペレーティングシステムとJavaのバージョンによって、出力が異なる場合があります。
「初期値がnullのオブジェクト initiallyNullString に対して、クラス名を返すStringメソッドgetClass()を呼び出そうとしているため、java.lang.NullPointerExceptionのエラーが発生します。」
Note
エラーを解決するには、最初にNullの値をもう一度指定する必要があります。
- String initiallyNullString = null;
- initiallyNullString = “not null any longer”;
- System.out.println(“The class name is: “ + initiallyNullString.getClass());
新しい修正済みのコードは、以下の出力を表示します。
initiallyNullString ==> null initiallyNullString ==> “not null any longer” The class name is: class java.lang.String
上記の出力は、最初はnullであるinitiallyNullStringが、その後「not null any longer」という文字列を含む新しいStringオブジェクトになる様子を示しています。次に、生成されたオブジェクトに対してgetClass()メソッドが呼び出されると、java.lang.Stringが返されます。ここで、Stringはクラス名であり、java.langはそのパッケージです。最後に、「クラス名は: class java.lang.String」という、完全で意味のあるメッセージが出力されます。
null値宣言は、古いコードではより一般的です。変数を最初に作成し、後で実際の値を割り当てるために使用されることがよくあります。通常は、後者を決定する一定の論理を経ています。ただし、Javaバージョン8以降では、以前にnullが使用されていた場合により適した新しい参照型Optionalがあります。
ローカル変数の型推論
これまで、Javaでは一般的なデータ型を使用して変数を定義してきました。しかし、Java 10では「ローカル変数の型推論」と呼ばれる新機能が導入されました。この機能を使うと、新しい変数の前にキーワードvarを使用することができます。この機能により、Javaはローカルの文脈からデータ型を自動的に推論(つまり、自動的に予想)します。型推論は、以前に説明した変数の定義の冗長性とは対照的であるため、議論の的となっています。このような機能の利点と欠点は議論の余地がありますが、事実として、C++などの静的型付け言語も型推論をサポートしています。
どの場合でも、型推論はデータ型の使用を完全に置き換えることはできません。なぜなら、型推論はメソッド内のローカル変数にしか適用されないためです。varを例として見てみましょう。
- var hello = “Hello”;
- System.out.println(hello);
通常の方法でコンソールに表示し、期待通りに機能するか確認するために、varキーワードを使用して変数helloを宣言します。
hello ==> “Hello” Hello
この例は、Javaインストール(具体的にはJDK)のバージョンが10以上であれば機能します。varキーワードは古いバージョンではサポートされていません。
コンパイル時に型推論が行われます。コンパイルプロセスでは、プレーンテキストのソースコードがマシンコードに変換され、型推論を含むさまざまな最適化が適用されます。これにより、型推論された変数に適切なシステムメモリ量が利用可能となります。したがって、コンパイル後に実行するマシンコードは、すべてのデータ型を手動で指定したかのように完全に最適化されます。
この例では、変数がローカルであるため、varキーワードは機能します。また、varデータ型はローカル変数にのみ適用されます。ローカル変数はメソッド内で定義され、そのメソッド内でのみアクセス可能です。これが「ローカル」と呼ばれる理由です。
varがローカル変数にのみ使用できることを示すために、次のようにmainメソッドの外に配置してみてください。
- public class Hello {
- var hello = “Hello”;
- public static void main(String[] args) {
- // example code
- }
- }
以上のコードをjshellに貼り付けると、以下のエラーが表示されます。
| Error: | ‘var’ is not allowed here | var hello = “Hello”; | ^-^
メソッドの外側にあるため、変数 var はそこで許可されていません。したがって、ローカル変数として認識されなくなります。そのため、非ローカル変数に対して型推論は機能しません。なぜなら、コンテキストを信頼してデータ型を検出することができないからです。
「var」を使用することは難しい場合もありますが、必須ではなくても、おそらく遭遇することがあるので、それについて知っていることは役に立ちます。
予約語
Javaで変数を宣言する際には、もう一つ重要なルールがあります。変数名に使用することができない予約語が存在します。例えば、次のようにint型のプリミティブとしてnewという名前を宣言することはできません。
- int new = 1;
もしこの例を試すと、newは予約語であるため、コンパイルエラーが発生します。
| 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; | ^———-^
「新しい」というキーワードは新しいオブジェクトを作成するために使用されますが、Javaはこの位置でそれを期待していません。前の出力のエラーリストでは、最も重要な部分は最初の部分です。
| Error: | ‘.class’ expected | int new = 1; | ^
エラー「.class」は、newキーワードを使用する際にJavaがクラスが続くことを期待していることを意味します。この時点で、Javaは文を解釈することができず、その後のエラーが発生します。
抽象(abstract)、継続(continue)、デフォルト(default)、for、そしてブレイク(break)など、他の予約キーワードもJavaでは特定の意味を持ち、変数名として使用することはできません。予約キーワードの完全なリストはJava言語キーワードのページで確認することができます。予約キーワードを全て覚えていなくても、コンパイルエラーを使って問題を特定することができます。
結論
このチュートリアルでは、Javaのプリミティブデータ型と参照データ型について学びました。これは複雑ですが重要なトピックです。じっくりと練習し、例を何度も確認してください。いくつかのデータ型や値を変更してみてください。成功したコードの実行には、エラーが発生する場合としない場合に注意を払いながら、感覚を養ってください。
Javaに関する詳細情報は、当社のJavaでのコーディングシリーズをご覧ください。