Javaの難しいところ

Javaをプログラム未経験者に教えるときの話。
細かいところまでちゃんと理解するための難しさではなくて、とりあえず頻出コードが読み書きできるまでの難しさの話です。細かいところまでの理解、どの言語も難しいので。
あと、ここではプログラム自体の難しさは別の話、ということで。
で、Javaには難しいところが結構あるんですけど、難しいのをひとことでいうと「昔の事情や歴史的経緯により、が多い」ところです。

プログラムを教えるときに何が難しいか

たとえばpublic static void mainを書くとか、おまじないが多いとか記述量が煩雑とかは、ツールで対処可能で、ツールで対処可能というのは機械的に慣れればいい部分なので、そこまで問題にならないと思います。
あと「おまじないを減らしたい」というのは教える側のこだわりであって、理解しやすさとは別で、そのおまじないがどういうときに必要かというところさえ理解できればいいと思います。ないほうが楽ではあると思いますけど。
ツールの導入もインストーラダウンロードして「次へ」を押しまくるのは、そう問題じゃないと思われ。


つまり「慣れ」ではなくて「理解」が必要なところに難しさがあると思います。
そういう、プログラムの学習が難しくなる要因として、プログラム自体の本質的は難しさとは別に、大きく2つ、次のようなものがあると思っています。

  • 同じことを実現するために、異なる2つの機能がある
  • 暗黙のルール

同じことを実現するために、異なる2つの機能がある

シンタックスシュガーは除き、同じことを書くときに2つの選択肢があるのは結構つらいです。
Javaが難しい部分の多くは、こちらになると思います。

  • classとinterface
  • intとInteger(基本型とラップクラス)
  • Listと配列
  • forとforEach
  • DateとLocalDateTime

まあ、最近はインタフェースをimplementsする機会は減っているし、自分で定義する機会もあまりないので、必要になったときには こう書け、って感じでいい気がします。型として使う分には、クラスと区別する必要はないし。
forとforEach、DateとLocalDateTimeも、好みやシチュエーションの問題ではある。


ただ、基本型とラップクラスや、Listと配列は、普通にコードを書くときについてまわるし、Streamを使うときには意識する必要があるので、ちょっと困る。
Streamがなければ、「配列は使わない」「ラップクラスはGenericsで指定するとき用」くらいの雑さで回避することができたのだけど。
Valhallaでどうにかなりそうなのが救い。

暗黙のルール

書いたコードの裏でいろいろ行われて、知らなければ書けないとかエラーが意味わからなくなるとか、そういうの。
Javaの場合はコンストラクタとラムダ。staticメンバとインスタンスメンバ。デフォルトスコープ。


コンストラクタの場合、暗黙でsuper()が呼び出されるやつと、引数ありのコンストラクタを定義するとデフォルトコンストラクタがなくなるののコンボで、意味不明なコンパイルエラーがでるのが罠でした。

class Foo{
  Foo(int a) {}
}
class Bar extends Foo{
}

で出るコンパイルエラー。
最近は継承使う機会も減っているので、逃げれる気はする。


staticメンバとインスタンスメンバもつらいな。まあ、入門時にはぜんぶ非staticで。
スコープ(可視性)も最初はあんまり意識しなくてもいいか。


しかし、ラムダ、おまえだけはだめだ。
未定義メソッドが1つのインタフェースに対してそのメソッドのシグネチャを使って、引数ひとつのときにはカッコや型が省略でき・・・、というの。
まあ、現実的に出てくるのはFunction、Runnable、Consumer、Supplierくらいなので、そこだけ押さえて、あとはMap.forEachみたいなのは「こう書けますよ」くらいでいいのかな。
いけるかな。
というか、そう逃げるしかない気がするな。