知り合いの JavaScript 経験者が、Java を学びたいと言ってましたので、簡単な入門前メモをまとめてみました。
想定している読者
Web ブラウザで動作する、もしくは NodeJS 環境で動作する JavaScript コードを学んだことがある、もしくは書いたことがある方を想定しています。
きちんと学ぶにはJava入門書を読んだり、良質なコードを多く参照して真似したりすることが有効だと思います。ただ現時点で JavaScript の知識があるのであれば、Java との差分を確認してから学んだほうが理解が早まるのではないか?と思い、このメモをまとめてみました。
このメモが簡単すぎる、冗長すぎると感じる方は、素晴らしいまとめサイトである Java コード入門 をお勧めします。
開発環境
いまの時代、何らかの統合開発環境を利用することを想定しています。とりあえず想定しているのは、昔ながらの Eclipse (統合開発環境) です。入門本や記事は多くありますが、例えば以下のような記事はどうでしょう。
ただし事前に自分で学習する場合はともかく、仕事用のコードを書く方は、仕事先の環境を聞いて開発環境や、Java のバージョンなど合わせたほうが良いでしょう。
より手軽に試してみる
ちょっと Java コードを試したいだけであれば、オンライン上のサービスを利用するのもお手軽で良いです。
また Docker 環境があれば、Java 環境を含んだ Docker Image を実行することで、Java 開発環境をインストールせずに試すこともできますね。私も今年 docker-lambda を使うネタ とか VSCode + Codewindネタ (NodeJSですがJavaも可) とか投稿しているので、興味があれば参照してみてください。
ポイントとなる差分
コンパイラ言語
Java はコンパイラ言語、JavaScript はスクリプト言語とされています。これは確かにそうなのですが、以下の理由であまり気にしなくても良いとおもいます。
- JavaScript は js ファイルを直接実行する実装が多い
- それに対して Java は class ファイルにいったん変換(コンパイル)してから実行する実装が多く、それにひと間かかる
- ただし今どきの統合開発環境はこの「ひと手間」を勝手に実行してくれるので、開発者はあまり意識する必要はない
Java の場合は *.class という中間ファイルが実行時に自動で作成される、ぐらいの知識で十分だと思います。
更に最近の Java では *.java ファイルをそのまま実行できる (ように裏でこっそりコンパイルしてくれる) ので、もはやコンパイル言語かどうかも怪しいかも?です。コンパイル言語という出自から派生した、次の静的型付けだけ理解できれば大丈夫。
静的型付けと動的型付け
Java は静的な型付け言語で、動的な JavaScript とは異なります。型についてチェックが厳しいので、それを意識して書かないとエラーや警告が頻発して驚かされるでしょう。
TypeScript のような静的型付けのできる JavaScript 系言語の経験者は、このセクションはスキップしてOK。
動的型付けのほうが気楽に書けるので、個人的には好きです。が、仕事でコードを管理する場合など、大規模だったり、実装とテストを気楽に繰り返せない状況では、静的型付け言語のほうが結果として楽な気がしますね。
以下の記事がお勧めです。
- Java、C、C#、VB.netなどは「静的な型付け」
- JavaScript、Python、Rubyなどは「動的な型付け」
「静的~」は構造をコンパイル時にチェックする。「動的~」は構造を実行時にチェックする。
静的型付けのメリットはなんと言ってもコンパイルエラーが出ること。これに付きます。
これによって実行して初めてわかる問題をかなり減らすことができます。C言語のようにしょぼい型システムだと嬉しさなんてありませんが、JavaやScala, OCaml, Haskellなどだとかなり有用です。
なお、紹介した記事を全部理解する必要はなく、ざっくり読んで「ふーん」とおおまかに感じるだけで良いとおもいます。学習が進んだ後で、もう一度読み返すことを前提として紹介しています。
オブジェクト指向
JavaScript にも オブジェクト指向 の概念は あります が、必須の知識ではなく、実はあまり意識しなくてもコードを書けたりします。
それに対して Java ではオブジェクト指向は根っことなる部分ですし、より複雑な仕組みがあります。
とりあえず入り口としては、継承、インターフェイス ぐらいを理解しておけば良さそうにおもわれます。特にインターフェイスは JavaScript にないもので、かつ、地味なところで、わりとお世話になっていたりします。
インターフェイスとは、ある特定の機能を実現するために必要なメソッドのシグニチャ(名前や引数、戻り値の型)だけを定義したものです。処理そのものは持ちません。
簡単に言えば、クラス定義から実行されるコード部分を省いたものです。つまりはそのクラスの外部仕様を定義するものの、実際の処理内容は記載されていません。C言語であればヘッダファイル(*.h)に相当するものです。
更に Java にはクラスとインターフェイスの中間のような抽象クラスもあり、「部分的にコードを省いた」クラスという感じです。
インターフェスや抽象クラスはそのままでは利用できず、継承(インターフェイスは実装)して、足りない処理コードを補ったクラスを定義する必要があります。と言うと難しそうですが、実は最初は、それほど気にしなくても大丈夫です。
例えば Eclipse では、クラス新規作成時に、以下のようにメソッドの自動生成がデフォルトでチェック済みなので、コード実装がされていない関数(メソッド)があれば、自動的に定義部分も作成しておいてくれます。間違って消したり、実装し忘れがあっても、警告やエラーがしっかり出ますので、すぐわかります。
メソッドのオーバーロード
Java コードを読んでいると、同じ名前の関数(メソッド)が幾つも定義されていて、どれを呼んでいるのか迷子になることがあります。これは メソッドのオーバーロード によるものです。
Javaでは、「同じ名前で、引数の型、並びだけが異なる」メソッドを複数定義することもできます。これをメソッドのオーバーロードと言います。
JavaScript だと、単に関数名だけみて探せば良いのですが。Java の場合は同じ名前の関数(メソッド)が複数定義されている場合、それらのなかから引数の数や型(継承関係も含む)が一致するものを探さないといけません。
まあ統合開発環境だと、このあたりもチェックしたうえで参照元に移動できますので、それほどは困らないのですが。例えば Eclipse だと、関数を呼び出しているところで F3 キーを押すと、それを定義したコードに移動できます。
文字列の比較
Java 初心者が最初にひっかかる点が「文字コードの比較」ミスです。以下のような例が有名ですね。
String str1 = "123";
String str2 = "123";
String str3 = "12" + "3";
String str4 = "12";
str4 += "3";
System.out.println(str1 == str2); // true (1
System.out.println(str1 == str3); // true (2
System.out.println(str1 == str4); // false (3
System.out.println(str1.equals(str4)); // true (4
JavaScript だと文字列の ==
演算は値の比較になるので、上記は全部 true になります。
Java だと基本的に ==
は「同じオブジェクトかどうか」の判定になり、値の比較には equals
メソッドを使用します。
ここでやっかいなのが、(1 と (2 は多くの環境で true
の結果になることで、バグ発生の原因となり得ます。これは Java コンパイル時の最適化による影響です。
Java で文字列は immutable (イミュータブル/不変)なもので、プログラム実行中にその値は変わりません。であれば "123"
という同じ値の文字列であれば、内部的に「あれ、これ真面目に2個用意しなくても、1個用意して使いまわせばいいよね?」という最適化(手抜き)をします。その結果 (1 が true
になります。
更に最近の Java コンパイラは賢いので、"12" + "3"
という式がコード中に含まれると「あれ、これ結局 "123" という文字列を使いたいんだよね?」と察してしまい、勝手に式を "123"
という結果に置き換えてしまいます。なのでその結果、(1 と同じ理由で (2 も true
になります。
なおこの結果は Java8 でのコンパイル結果です。その後のバージョンのもっと賢い Java コンパイラは (3 も true
になる最適化をしてくれるかもしれませんねw
というわけで、Java においては値の比較は equals
という関数を使うこと、お忘れなく!
また JavaScript にもある概念ですが、Java の ラッパークラス についても、一応は確認しておくと良いでしょう。
言語の拡張について
Java 言語は古い、化石のような存在である、といった印象をもつ方も居ると思います。Java は最新の言語仕様をわりと積極的に取り入れており、特に、JavaScript にどんどん影響されてきています。以下、個人的な感想も含みます。
JavaScript は Java の影響も強く受けているので、Java の子供みたいなものです。そして Java は JavaScript の良いところを順に実装してきています。順に機能が追加されているため Java のバージョンには注意が必要で、詳細は Javaバージョン履歴 を参照してください。
ただし Java は過去との互換性も重視しており、抜本的な構造改革は難しいので、かなり強引な実装になっている部分もあります。新機能を単に使う分には良いのですが、その実装部分に踏み込むとかなり難解な部分もあるので、細かな部分が気になっても、探求は学習が進んだあとに取っておくのをお勧めします。
実装の工夫例
Java にも 無名クラス があり、例えば以下のように利用できますが、
import java.util.*;
public class Test02 {
public static void main(String[] args) {
new Thread(new Runnable() { // ココ
public void run(){
System.out.print("Runnable.run");
}
}).start();
}
}
過去との互換性を維持するため、つまりは無名クラスなど知らない実行環境のため、コンパイル時には以下のような class ファイルが自動生成されています。コード上は無名クラスとして記述されていますが、実際には内部で名前(今回は Test02$1)が付与されているわけです。
※ 私も最初、この謎の class ファイル群はなんだろう?と不思議に思っていました
なお余談ですが JavaSE 8 からは新しい記述の ラムダ式 が使え、より JavaScript っぽい表記ができます。
List<String> list = new ArrayList<>(Arrays.asList("aa", "bb"));
list.replaceAll(str -> str.toUpperCase());
System.out.println(list); // [AA, BB]
まだ馴染みの薄い拡張
上記のとおり Java 言語は拡張されてきていますが、まだ馴染みが薄いといいますか、わりと先進的な Java 開発の現場でないと見かけない要素もあります。
さきほどのラムダ式もそうですが、JavaScript では普通の書き方でも、Java 言語のなかで使用すると違和感が出てくることもあるでしょう。バージョンの関係で使えたとしても、です。既存のコードを読み、また先輩の開発者に聞いて、雰囲気をみて使いましょう。郷に入っては郷に従え、です。
個人的には ジェネリックス はもう普通に使われているケースが多く、使っていないのは、ものすごく古いコードだけだと思います。
アノテーション はかなり一般的になったと感じますが、独自アノテーションを定義するまで活用する例はまだ少ないと感じます。
Stream API は JavaScript だと多用する書き方ですが、Java のコードではまだあまり積極的に利用されていないように感じます。まあこれは、言語というよりライブラリ設計の話題な気もしますが…
※ これらは案件、現場によって大きく異なるので、繰り返しますがあくまで個人の感想です
ライブラリは大事
どの開発言語でもそうですが、言語ごとの差異はそれほど大きくなく、どちらかというと習得に時間がかかるのはライブラリの把握と上手な利用方法です。
Java は標準ライブラリがなかなか豊富です。まずか以下のような公式の API 仕様を参照してみましょう。とはいえ全部を読むのは難しいため、クラス利用時に詳細を確認しつつ、少しずつ理解の幅を広げていくのが良いでしょう。
- Java(tm) Platform, Standard Edition 8
API仕様 - Java® Platform, Standard Edition & Java Development Kit
バージョン11 API仕様
java.lang が標準のライブラリで、設定しなくても最初から、自動的に読み込まれます。もし、ちゃんと読んでいくのであれば、最初はここからかな?
JavaScript の場合、NodeJS なら npm サイト で探せば、必要な機能はたいてい見つかるものです。Java の場合はまず Maven Repository を探すのが良いでしょう。各バージョンのコンパイル済みライブラリ(jar)ファイルの入手もできます。
なお NodeJS の npm はダウンロードと設定を同時に実行してくれますが、Java の場合は Java 実行環境から見つかるようちゃんと配慮してあげる必要があります。コマンドラインで実行している場合はクラスパスを設定してあげるか、-cp もしくは -classpath オプションで配置した場所を明示する必要があります。Eclipse の場合はプロジェクトの Java Build Path 設定を確認して、見当たらなければ追加してあげましょう。
JUnit など有名どころは Eclipse 環境で既に用意されている場合もあるので、まず先に下の Add Library ボタンを押してみるのも良いかもしれません。
さあ学習を始めましょう
と、最初に気になりそうな点をざっと書いてみました。あとは実際に Java の学習を始めてください。個人的なお勧めは、やはり Java コード入門 サイトです。
プロググラミング自体がまだ不慣れで、より優しい入門書を読みたい場合には スッキリわかるJava入門 第3版 スッキリわかるシリーズ など良いかもしれません。
逆に自信のある方は上記の Web サイトなどで理解した後、資格取得も視野にいれ、バージョンにあわせて以下のような書籍で学習するのも良いかもしれません。
Enjoy!
以上、JavaScript 経験者へ、Java を学ぶ際に知っておいたほうが良さそうなことをまとめてみました。これから Java 言語に親しんでいく際の、敷居が少しでも下がったのであれば嬉しいです。
逆に、よけい混乱させてしまったのであれば、すみません。文章は読み飛ばし、ご紹介した各種リンク先だけでも、参照いただければと思います!
私自身は Java -> JavaScript と学んだ人なので、見落としなど多いとおもいます。記述もいいかげんだったり、まだ十分ではないと思いますが、気がついた点があったら修正・追記していきたいとおもいます。
それではまた!