負け犬プログラマーの歩み

負け組だった人間が今一度PGとして人生の飛躍を模索するも加齢と共に閉ざされる未来に直面しているブログ。

なんかC#6.0がそろそろ登場するらしい

書ける言語の数が両手で足りる人間が好きな言語を語るのもどうかと思うが、C#は好きな言語のトップ3には間違いなく入る。だけど悲しいことに、ここ1年ぐらいC#でコーディングする機会がなく、専らRubyJavaScriptVB(しかも自由に改行できない2008!)ばかり書いていたので知らなかったのだが、アンダース・ヘルスバーグ曰くC#6.0はコンパイラC#で書き直して出すとのこと。
http://wesnerm.blogs.com/net_undocumented/2013/07/anders-on-c-60-at-build-2013.html

んで、更にググって見るとC#の開発者であるマッツ・トルゲルセンという人がなんか昨年の12月にC#6.0の導入を検討されている新機能を語ったという情報がある。
http://adamralph.com/2013/12/06/ndc-diary-day-3/#more
http://damieng.com/blog/2013/12/09/probable-c-6-0-features-illustrated

ここまでの情報を組み合わせると、もう今年の話なんだが、2014年にC#6.0(とVB2014?)が出るらしい。以下はほぼ転載気味で検討されている新機能を見てみる。

プライマリーコンストラクタ

クラス定義とコンストラクタを合体して書けるようになる模様。原文ではconstructorsとsがついてるのでこのシンタックス複数定義できるっぽい。F#やScalaには既にある機能だとか。
C#5.0まで

public class Point
{
    private int x, y;

    public Point(int x, int y)
  {
        this.x = x;
        this.y = y;
    }
}

C#6.0

public class Point(int x, int y)
{
    private int x, y;  
}

Readonlyな自動実装プロパティの新記法

一つ疑問なのは、この記法の場合コンストラクタで実行時に値を変えることは可能なのだろうか。そうでない場合そもそもconstに比べた場合のメリットが希薄なように思えてならない。
C#5.0まで

private readonly int x;
public int X { get { return x; } }

C#6.0

public int X { get; } = x; 

静的クラスのusing

C#5.0まで
現在の名前空間に静的メソッドを全て持ってこれるらしい。string.IsNullOrEmpty(obj)IsNullOrEmpty(obj)になるのは地味に嬉しい。原文ではメソッドに限定しているがメンバも持ってこれると助かるのだが…。

public double A { get { return Math.Sqrt(Math.Round(5.142)); } }

C#6.0

using System.Math;
...
public double A { get { return Sqrt(Round(5.142)); } }

プロパティ定義式

悪くないんだけど、この記法でsetとgetの両方を定義することはできるのか?
C#5.0まで

public double Distance { get { return Math.Sqrt((X * X) + (Y * Y)); } }

C#6.0

public double Distance => Math.Sqrt((X * X) + (Y * Y));

メソッド定義式

この例の場合は一行メソッドだから行数減ってインパクトあるけど、複数行のラムダ式になる場合はそもそも意味あるのだろうか…。
C#5.0まで

public Point Move(int dx, int dy)
{
    return new Point(X + dx1, Y + dy1);
}

C#6.0

public Point Move(int dx, int dy) => new Point(X + dx, Y + dy);

可変長引数はIEnumerable型でも渡せるように

でも渡した先でどうせ配列に直すんじゃそれほど変わらないようにも思えるのだが…。
C#5.0まで

public void Do(params int[] values)
{
 ...
}
Do(someEnum.ToArray());

C#6.0

public void Do(params IEnumerable<Point> points)
{ 
  ...
}
Do(someEnum);

モナドぬるぽチェック

関数型言語は全く知らないのでモナドが何かよく理解できないのだが、要はRails(≠Ruby)で言うhoge.try(:points).try(:first).try(:x)みたいなことをやりたいのだと理解する。
C#5.0まで

if (points != null)
{
 var next = points.FirstOrDefault();
  if (next != null && next.X != null) return next.X;
} 
return -1;

C#6.0

var bestValue = points?.FirstOrDefault()?.X ?? -1;

ジェネリッククラスの型パラメーターの推論

これは確かに自明なんだから推論できるはずだ。ただ、いい加減メンバー変数にも型推論を入れて欲しいのだが…。
C#5.0まで

public MyClass<T1, T2> Create<T1, T2>(T1 a, T2 b)
{
    return new MyClass<T1, T2>(a, b);
}
var x = MyClass.Create(1, "X");

C#6.0

var x = new MyClass(1, "X");

out引数のインライン宣言

これは気になっていたので地味に嬉しいのだが、個人的にこのxって直感的にスコープが違うように脳が判断してしまうので、後の行で直接参照するのも少し気持ち悪い気がする。そもそもTryParseboolを返すこと自体がおかしいんじゃないのか。
C#5.0まで

int x;
int.TryParse("123", out x);

C#6.0

int.TryParse("123", out int x);


ここまで見た感想として、個人的にC#3.0とC#4.0で大きく言語仕様が拡大されたのにC#5.0では変更点が拍子抜けする程少なかったので、細かいながらも色々と糖衣構文を盛り込もうとしているC#6.0は歓迎したいところではある。一方で「この程度か…」という気持ちもまた芽生えてしまう。ScalaとかC++とかC#より機能が多めとされている静的型付け言語もあるようだし、まだまだ他の言語からパクれる要素は残ってるのかもしれない。まあパターンマッチ導入以前に、いい加減switch文のbreak;必須を撤廃するとかして欲しいんだがなあ…。

リクナビNEXT - エンジニアが絶対に利用してはいけない転職サイト

エンジニアは比較的転職しやすい職業のように思う。需要は常にあるし、技術と実績次第で割と何処でも働けるから。しかし、転職活動をする際の所謂「転職サイト」の選択については少し慎重になった方がいいかもしれない。かくいう俺自身は実は転職サイトは3~4個しか利用したことがない。でもその中でも1つだけ群抜けて「これは絶対に利用してはいけない」と言えるサイトがあったのでそれについて書きたいと思う。まあタイトルでも言っているように、リクナビNEXTなんだが。

Q.なぜリクナビNEXTを絶対に利用してはいけないの?

A.自動配信メールと人売り紹介料狙いの転職エージェントからのコピペメールしか来ないから。

転職者は「自分の思い浮かぶ将来像」を踏まえて、自己PRやキャリアプラン、過去から今までの膨大な案件の概要や開発環境・技術、役割、成果などについて「どう書けば自分の思いが伝わるだろう?」「どうすれば私の良い所や実績が伝わるだろう?」と一生懸命に考えてレジュメを書いている訳だけど、そんな物はリクナビNEXTでは一切読まれていない

というのも、企業はただキーワードで機械的に引っ掛かった人間に対し大量に同じ文面のコピペメールを送りつけて、それに喰らいついてきた人達と大量に面接し、光る人間だけを拾いたいと考えている。実際、2~3年リクナビNEXTを利用してきて、レジュメを読んでコピペではないオファーをくれたのは1社しかない。割合にしたら1%にも遠く満たない。例えば「PHPの経験はありませんが…」とレジュメに書いたら、「PHP」で検索してきた企業から「特に会員番号 114514893様のPHPによる経験に興味を持ちました」というメールを貰うことは普通にある(実話)。

そもそも一連のメール送信は企業でなくリクナビNEXTが行うらしい。仕組みとしては、企業の為に一定回数(恐らく3回まで)のコピペメール送信プランを有料で買わせて、リクナビNEXTが支給するメールテンプレートにメールを書かせて、メール爆撃対象にする検索条件を指定させるというのが実情だろう。だからどの企業のプライベートオファーにも似たような文面が含まれているし、以下のような流れを毎回辿るわけだ。

①1回目のプライベートオファー。内容は完全コピペ。会員番号が違うだけ。
                  ↓
②返事がない場合2回目のプライベートオファー。内容はコピペ+「先日の私からのオファーはお読みいただけましたでしょうか」などリクナビNEXT指定の決まり文句。
                  ↓
③返事がない場合3回目のプライベートオファー(これは無いこともある)。今回はタイトルもリクナビNEXT指定か大体「これが最後の連絡です」と入っているが、内容は相変わらずコピペ。

こっちは一生懸命レジュメを書いているのに、金を取って参加させている企業にコピペメールを大量一括配信させるだけとは、何とも酷い運営ではないかと思う。しかも複数回に渡って、こちらに返事が無いことを責めるような文面を指定してまでコピペメール配信の仕組みを設けてるのは、流石に醜悪と言える。

挙句の果てに、リクナビNEXT通知メールという名前で、オファー着信の度に1通、更に期限到来が迫るともう1通自動でメールを送信して来る。しかも、これはオフに出来ない。つまり、一旦一つの企業のメールの爆撃対象に選択されたら、メールボックスには最終的に6通のメールが入ることになる。

プライベートオファーとはまた別に興味通知オファーというものもある。企業のコピペに興味を示した人が返事をできるという仕様であるが、「興味がない」ことを転職希望者が伝える機能はなく、興味が無い場合は無視するしかない。しかし、無視をしたらリクナビNEXTから「未返信の興味通知オファーがあります!」とか通知メールと、更に企業の「先日の私からの…(以下略)」のメールが何通もやってくる。興味ねーから返事がないんだろ!と怒鳴りつけたく鳴る。

スパムメールの定義はWikipediaによるとこうである。

スパム (spam) とは受信者の意向を無視して、無差別かつ大量に一括して送信される、電子メールを主としたメッセージのことである。

こうして見ると、リクナビNEXTの所業は最早スパムメールに近い。登録している転職希望者をスパム攻撃する権利を企業に売り渡して、自分たちが受け取らないことを選択できない自動通知メールの類を強制的に自動送信するのだから。もちろんスカウト設定をオフにすればコピペメールの爆撃は終わるのだが、それだと一生懸命レジュメを書いてスカウトを待つ意味がそもそも消失する。まさに「利用するのを止めるのか、スパムメールを我慢するのか」の二択がリクナビNEXTには求められる。

リクナビNEXTの真の闇

しかし、リクナビNEXTが本当に醜悪なのはここからだ。企業からのコピペ爆撃は序の口というか、回数制限があり基本的に1回だけなようだ(もちろんその1回で3通までコピペメールを出せる訳だが)。ところが、リクナビNEXTと提携している転職エージェントに至っては、それこそ無制限に何通でも出せるようになっており、断っても断っても断っても断っても、転職エージェントが自由に粘着できる仕様になっている。

転職エージェントは、みな必死に無料であることを強調するが、それは嘘に近い。実際、彼等は無償どころか、エンジニアを1人転職させるだけで数百万の対価を得るのだ。どんなエンジニアでも都会で転職したら年収400万程度にはなるだろうし、その1/4と考えたら100万円の紹介料が転職エージェントには入る。費用はほぼ人件費しか掛かってないし、その人件費も面談に転職エージェントが同行することは基本的に一切なく、派遣会社の顔合わせ程度の外回りもない。毎日数十人の転職希望者を突撃させてはその結果をメールや電話でやり取りして待っていれば、自分たちに数百万の紹介料をもたらすことになる優れたエンジニアも出てくる。考えれば考える程ボロい商売のように思える。

つまるところ、リクナビNEXTは、リクルートや転職サイトの名のもとで人様のレジュメをかき集めたあと、転職エージェントから使用料を徴集した上で「狩らせる」のである。週に複数回複数の担当者が、とにかく転職希望者を自分たちの会社経由で転職させて紹介料という名のマージンを得るために、今日も明日も明後日もひたすらコピペメールを人様のメールボックスに送りつけてくる。

下の画像を見ればその惨状が少しはお分かりいただけるかもしれないが、これがリクナビNEXTに登録したあとのメールボックスだ。言うまでもなく中身は手書きではなく100%コピペのスパムメールであり、しかも数日前にメールを送りつけてきた転職エージェントが再送してくることも実に多く、閉口するしかない。
f:id:Cedille:20140325202418p:plain

あまりにも稚拙、拙速にコピペメールを送りつけるあまり、冬なのに夏の時候の挨拶を間違えてコピペしてくるようなバカすらいるのはもう笑うしかなかった。

そんなに紹介料に必死になるのであれば、せめて登録者のレジュメや希望条件などを少しは読んで相手の興味をひける文面を作った上でメールを出せばいいのではないかと思うのであるが、ほぼ全ての転職エージェントは「返事がないなら返事をするまでコピペメールを爆撃してやる」としか思っていない。

なお、リクナビNEXTで際立って酷いワークポートとプレアデス(この二社に関しては、機会を設けて別の記事を投稿する)のうち、片方にクレームを入れたら電話口に出た男性の担当者は、「10数回送ったらやっと返事を頂けた人もいるので…(これからもスパム攻勢を続けます)」と悪びれる様子もなく答えていた。

提携転職エージェントの実際のサービスは?

一言でいうと、悪い。

仮にスパムメールの件があっても肝心の転職支援サービスが良ければ許せる訳だが、そもそも転職エージェントは、転職希望者とはまた別の利害関係を持つ側なので、本質的に「自分達の紹介料マージン > 転職希望者の将来」という立場にならざるをえない。

ワークポートとギークリーの2社ほどに以前登録していたけど、とにかく「数撃てば当たる」という感じで、週に何社もエントリーさせては玉砕させてくる。面談に当たってのアドバイスなんかないし、そもそも担当者は非エンジニアだから、エンジニアの気持ちなんて分かるはずがない。

労を惜しむ傾向にあるのは特に強く感じる。企業にコンタクト取るのを基本的に面倒臭がる。こちらの転職可能時期について企業に伝えたいことがあったのに「そんなの別に話さなくていいですよ」とか笑って伝えてくれず、結局、即日から就業可能を期待していた企業に、面接の直前で来月以降まで動けないと話したら、その場で即不採用になったのが一番ムカついた。

もちろんエージェント経由で幸せな転職を経験した人もいるだろう。それは否定しない。でも俺は技術と経験は浅い方かもしれないが、エンジニアとしての夢や理想があり、自分のキャリアプランがあり、それを拙いながらも綴らさせて頂いてるのだ。それを完全に無視してスパム爆撃してくるような輩に自分の将来を委ねるつもりはない。

まともな転職サイトとは?

ここまで書いたあと、一部に「だったらお前はリクナビNEXTより良い所を知っているのか?」と思う人もいるかもしれないが、実際にリクナビNEXTが際立って、というか「リクナビNEXTだけが極悪に悪い」という印象しかない。試しに、下のサイトを紹介してみる。

Find Job!
http://www.find-job.net/

俺が利用した限りでは、たまにコピペメールも来たりもするが、大半のオファーはレジュメを精読した上で一つ一つ手書きで来ており、また非常に態度や物腰のいい企業の人事が利用していると感じる。ここ経由で転職したことはないし、宣伝の意図もないから別に利用を推奨はしないけど。

なぜJavaはC#と比べて駄目なのか

Javaは決して悪い言語ではない。

C++からポインターの「*」やアロー演算子の「->」とかスコープ演算子の「::」とか気持ち悪いものを廃止・整理して、比較的読み易いシンタックスになったと思う。1995年当時から見れば、十分に出来の良い言語だったと思われる。

でも後発のC#でコーディングする機会が増えてきたら、如何にJavaが駄目(というか保守的な)言語かってのもまた同時に痛感してしまう。2005年リリースの2.0の時点で既にJavaをほぼ完全に上回っていると思うのに、その後ラムダ式LINQ・拡張メソッドなど数多くの新機能が加わった現行C#とは最早比べるまでもないと思う。

以下は根拠。

■注(2014年2月18日)
このエントリーは殴り書きに等しい状態で放置してましたが、最近は思わぬところで読まれ始めたりしたので、ちょっと加筆修正しました。

①そもそも純粋なオブジェクト指向言語ではない。

Javaで実際に用いる型の大半はプリミティブ型だけど、このプリミティブ型はJavaのルートオブジェクトであるjava.lang.Objectを継承しているオブジェクトではない。その代償として、メンバやメソッドを一切を持てず、int型の変数を文字列にするのにインスタンスObject.toString()がないから、クラスメソッドInteger.toString()を使う必要がある。後述する型消去型のジェネリクスとの相性も最悪、というかそもそも使えない。
RubyScalaみたいな純粋なオブジェクト指向型言語のデザインを採らず、敢えて中核的な型に「オブジェクトではないもの」を残したのは、パフォーマンスの観点からと言われている。すなわちオブジェクトは、ヒープ領域にメモリを確保するし、負荷の大きいガベージコレクション処理を要してしまうからだと。
一方で、プリミティブ型に該当するC#のデータ型は、もちろんスタック領域に作られる構造体であり他のクラスが継承することはできないけど、System.ValueType(この親はSystem.Object)を継承しているオブジェクトであり、基礎的なメソッドやメンバーは備わっているので、パフォーマンスとオブジェクト指向を見事に両立させたデザインとなっている。

※ただし、仮にJavaのプリミティブ型がC#のデータ型よりも高速に機能するのであれば、この仕様は正当化されると思う。ベンチマークもないからなんとも言えんけど。

2015/1追記:簡単な計測をしてみたところ、同じぐらいかむしろC#の方が速い模様。
JavaとC#のパフォーマンスを比較する。 - 負け犬プログラマーの歩み

②多重継承でなくインターフェイスを採用したのに、ミックスインがない。

多重継承は色々と問題が有る(らしいけどC++の経験がないので知らない)。だから継承できるクラスは1つのみだけど、その代わり「これを実装したものは外部から見れば全て同一と見なせる」って感じのインターフェイスという仕様を採用している。ただしインターフェイスは、デフォルトの実装を持たないので、インターフェイスを実装したクラス達が共通の処理を持ちたくても、基本的にコピペするか委譲させるしかない。また、部分的に実装するという選択もできない。その欠点を補うものとして他の言語はミックスインやトレイト、@optionalを採用し、C#も拡張メソッドという形でこの欠点を克服したのだが、Javaは近日登場予定のSE8でやっとインターフェイスのデフォルト実装を導入するまでずっとこの問題を抱え込んでしまっていた。

③関数が第一級オブジェクトではない。

これは①の完全なオブジェクト指向型言語ではない理由の一つでもある。とにかくJavaは関数を引数として渡すことができない。やるとしたら文字列で関数名を渡してリフレクションするしかない。Javaでよく無名クラスを使わざる得ない最大の理由がここであるけど、無名クラスを使うとなると、今度はクロージャーがなかったりするんで、無名クラスに値を渡したり逆に値を受け取るために、finalをつけた変数をわざわざ用意することになる。

※これもJava SE8で変わるのかもしれないが最早Javaには興味がないのでよく仕様は見ていない。

ジェネリクスがあまり役に立たない。

Javaジェネリクスは型消去法で実装されている。要はコンパイルするときに、全てObject型に入れてパラメーターに与えた型でキャストするコードに置き換えてしまうやり方だが、①で述べたようにJavaで用いる型の大半はオブジェクトではないプリミティブ型なので、ジェネリクスはそのままでは使えず、わざわざIntegerなどのラッパークラスに対するボクシングの必要性が出てくる。
加えて、そもそもJavaジェネリクスは単純な型のミスマッチを検出できることしか利点がないように思う。あるコレクションに対して間違って別の型のオブジェクトを格納してしまうという凡ミスがビルト段階で分かる程度の話であり、名前から分かるようにジェネリクスの本来の意義である「総称的なプログラミング」とは程遠い使用用途になっている。C++のテンプレートから由来した仕様なんだろうけど、いっそのことジェネリクスという名称を用いるより、型の安全性の確保が主目的であれば、タイプセーフティとか何とかでいいだろう。
これに比べると、C#ジェネリクスはもちろん大抵の場合はコレクションの型の安全性を確保する場合に使われるだけかもしれないが、コンパイル後にも動的に型パラメーターを取得することが可能になっている。

⑤プロパティがない。

JavaC#の比較として良く「プロパティの有無が~」と指摘されるが、個人的には、オブジェクト指向言語としては、obj.Hoge = "foo"の方がobj.setHoge("foo")よりも幾らか直感的で分かりやすいシンタッタスであるとか、 Javaのコードは頻繁に末尾が...)));となって汚い程度の不満があるだけで、そこまで致命的な問題とは思わない。
そもそもC#のプロパティもコンパイルすればset_Xみたいなメソッドとして定義されるというのに、コンパイル前まではメソッド扱いされないのでデリゲートで指定することができないなど個人的に不満があるからだ(ラムダ式で回避できた記憶もあるが、これが原因でC#でもわざわざJava式のアクセッサーを書いた事もある)。
JavaもSetter/GetterはIDEで自動生成させるのが通例であるのなら、そこまでプロパティの有無でユーザの生産性が落ちるとは思えないし、それがJave SE7で導入寸前までいって見送られた理由なんじゃないかなとは思う。

⑥インデクサーがない。

C#の場合はコレクションや独自クラスには[]で中身にアクセスできるのに、Javaの場合はlist.get()になってしまう。またC#は独自クラスにインデクサーを定義して、[]でアクセスさせることができる。もちろんステートメントの末尾が頻繁に)));になるぐらいのデメリットしかないが、オブジェクト指向言語としてより直感的なインターフェイスを提供しているのはC#だ。

型推論が超限定的。

そりゃあ一部の言語の本格的な型推論に比べて、C#varは単に型が明らかなものを省略可能にしているので、型推論と呼ぶには若干の抵抗もあるかもしれないが、いちいちローカル変数に長ったらしい型を書かずに済むのは非常に助かるわけで、個人的にはC#3.0で導入された新機能のうち最も嬉しいものだった。
これに比べて、Java型推論List list = new ArrayList<>();のように、右辺の型パラメーターを<>に省略できる程度に限定されている。「<>がダイヤモンドに見えるからダイヤモンド演算子」とか呼んでいるらしいが、これがSE7の目玉仕様の1つだった時点でJavaがどんなに駄目な言語だったかを物語る。
もちろん、C#型推論はローカル変数限定となっているので(多分必ずしも代入を必要としないから?)、メンバー変数にコレクション等を指定する場合はJava型推論の方が優れているとは言えるが、どちらにしろ限定的である。

⑧いまだに名前付き引数や省略可能(オプション)引数がない。

厳密に言うと可変長引数を使えば省略可能にはできる。でも使用者にインデックスの意味を覚えさせるようなメソッドを設計して外部に公開するのはアンチパターンなので、結局はJavaはひたすらオーバーロードメソッドを復数定義し、その中でnullだった場合のデフォルト値を自分で設定するとか、名前付き引数がないから仕方なくオリジナルの引数用クラスを作るなどを迫られる。
コンパイラなんて書ける気がしない言語設計書の素人が言うのもなんだが、この2つに関しては基本的に糖衣構文なので、パーサを少しいじるだけで実装できるように思えるが、C#Rubyもそういえば実装がだいぶ遅れた経緯もあるので、きっと難しいのかもしれない。そもそもSun(Oracle)は基本的に保守的なので、オーバーロードがある限りはポジショナル引数のみの仕様にしたいのかもしれない。

メソッドがデフォルトでオーバーライド可能であり、オーバーライドを示すアノテーションが任意になっている。

Javaは「オーバーライド禁止の場合はfinalをつけて、オーバーライドする場合は@Overrideアノテーションを任意でつけられる」という仕様になっているが、C#の「オーバーライド可能な関数にはvirtualをつけて、オーバーライドする場合はoverrideキーワードをつける」という仕様に明らかに劣ると思う。とりあえず、手動でfinalを書くのはだるい。
Javaは1.6からはインターフェイスメソッドにも@Overrideをつけられるように変更した事は一見良い判断とは思うのだが、任意でよければC#だってインターフェイスの型名とメソッド名も記載し、明示的にインターフェイスを実装することもできるので、どの道Javaの仕様に優位点はない。
なお、Javaはこれだけオーバーロードに依存しているのだから、VB.NETではキーワードとなっているoverloadアノテーションなりで導入した方がいいとは思うのだが、C#にも実は存在しないので、これはおあいこ。

⑩チェック例外という概念がある。

昔からチェック例外については賛否両論があるので一概にJavaが悪いとは言えない。でもやはりJavaを書いてると、API使うときにコンパイルが通らないからとりあえずcatchブロックは用意したけどその中身が空っぽってことがよくあると思う。また、スレッドを使う場合は必ずInterruptedExceptionをキャッチしないといけないのは煩雑過ぎる。

partialがない。

partialはうまく使えば可読性を上げるし、ASP.NETでは自明で機械的な変数宣言の類はVisual Studioが自動生成したpartialクラス内でやってくれるのでソースコードも綺麗になったりする。しかし、Javaにはそんな便利なものはないので、例えばAndroidアプリを作っていると、クラスの上の方はいつも大量のView関連のメンバ変数を手動で定義しないといけない。

演算子オーバーロードがない。

個人的にC#でもRubyでもこれまで一度も演算子オーバーロードしたことないが、数理計算をしたい場合などには問題かもしれないし、また既に標準ライブラリの時点でこの欠落が影響している部分もある。例えば、C#は文字列同士の比較は普通に==演算子でいけるのだが、Javaの場合はStringがオーバーライドしたequal()を使わないといけない。==の「メモリでの同じ場所を指してるかどうかを判定する」という挙動を文字列という特殊なオブジェクトに対して変えることができない。

⑬デストラクタがない

C#のデストラクタもログを出力する時にしか使ったことはないけど、あえてJavaがデストラクタを採用しなかった理由もまた分からない。

2014/06/24
すいません、ファイナライザは有りました。

プリプロセッサがない。

C言語プリプロセッサだらけのコードは読んでてげんなりするが、それでも#ifすら使わない人でも流石に#Regionぐらいは大半のC#/VBエンジニアは使うだろう。でもJavaにはプリプロセッサはない。

■2014/06/24追加

⑮1つのファイルにpublicなクラスを1つしか含められない。

ファイル名とクラス名が一目瞭然なJavaの方が分かりやすさに寄与していると一瞬思うかもしれないが、それでも細かいクラスを1つの名前空間の元に含めて管理できるC#の方がやはり分かり易い。

⑯文字列の扱いが不便。

Javaは前述の==演算子で文字列を比較できないだけでなく、単に文字列に変数を埋め込むだけのString.format()は奇怪な構文を必要とし、逐語的文字列リテラルもない。

Decoratorパターンが遂に消滅する日がやってきた?

例えば、GoFデザインパターンのIIteratorでは、もうJavaC#では拡張forなりforeachがあるのでほぼ不要になっており意識して実装することも無くなってきていると思うわけだが、ここにきてDecoratorパターンもパターンとして意識する必要性が無くなってくるのではないかと思う。

というのも、Java SE8では、ラムダ式が遂に実装されることばかりがクローズアップされるが、実はインターフェイスのデフォルト実装が導入されるというかなり重大な変更も予定されている。構文はまだ変更されるかもしれないが、こんな感じになるらしい。

public interface Sample
{ 
    public Object doSomething()
        default { return null; } 
}

あのRubyの父親まつもとゆきひろも指摘していたらしいJavaの大きな欠点だった「実装を共有できない」というインターフェイスの欠点がここに来て修正されることになる。

Rubyの作成者であるまつもとゆきひろ氏は「多重継承の代わりにインタフェースを使ったのはJavaの賢いところ。ただし、実装の継承を落としたことはとても痛い」と指摘する。

従前は、実装の共有ができないインターフェイスだと、

public abstract class Fruit
{
    protected string name;
    protected Boolean isFruit();
    {
        return true;
    }
}

public interface IVegetable
{
    public Boolean isVegetable();
}

public class Vegetable implement IVegetable
{
    public Boolean isVegetable()
    {
        return true;
    }
}


public class Apple extends Fruit
{

}

public class Melon extends Fruit, implements IVegetable
{
    private IVegetable vegetable;

    public Melon(IVegetable vegetable)
    {
         this.vegetable = vegetable;
    }

    public Boolean isVegetable()
    {
        return vegetable.isVegetable();
    }
}

こんな感じに作るしかなかった。実装を共有するには、インターフェイスを実装したクラスを用意してそれに委譲させないといけないし、共有するクラスのすべてに、そのインスタンスを渡さないといけない。これはすごく手間暇がかかったものであるが、デフォルト実装が共有されるのであれば、もうインターフェイスはpublicのメソッドについては抽象クラスと同様の扱いができる。ただ、依然としてフィールドは定義できないどころか、メソッドはpublicしか定義できないんだがね。結局、インターフェイスって概念そのものが、外部から見たら同一のものとして見えるようにするってことだから。Javaは多重継承は認めてないつーのは変わらん。

MVVMを少し調べてみる

今働いてるC#の案件は、元々はPGx2人で作るはずが俺1人になったけど、それでも納期が死ぬほど甘いので、とにかく時間が腐るほどある。だから、WPFかつMVVMで作って見ることにした。失敗してもアプリケーションの機能が少ないから、Formで作り直しても間に合うし。初めてVisual Studio 2005より先のバージョンを業務で使えるので、LINQだのラムダ式だのの要素はひと通り使うつもり。.NET 4.0だからC#5.0の非同期処理は使えないけど。

で、AndroidWPFは少し似ている部分もある。MVVMで一度開発してみれば、自分が作ろうとしているMVC+PMのアーキテクチャーに何か新しい知見と着眼点をもたらしそうな気がするんだよね。MVVMは、MVPMの亜流と言われているが、門外漢にさらりと分かる詳しい解説もあまり無かったんで、実際に作ってみないと見えてこないことがある。

以下は開発してみた感想(非定期更新)

  • 巷ではコードビハインド、つまりWPFならXAML.cs、フォームならフォーム.cs、ASP.NETならaspx.csというファイルには、基本的に何も書かないというのが鉄則みたい。WPFでも稲妻マークをクリックして即座にイベント発行できるし、かなり便利なイベントもあるのにコードビハインドに記述禁止って、生産性を下げないのだろうか。
  • ただ、MS推奨がどっちなのかは知らんが、MVVMでの開発に使えるような機能やクラスライブラリが予め用意されているのは事実。イベントドリブンだけの開発しか想定されていないであろうAndroidとはそこが違う。その最たるものがバインディング
  • しかし、そのバインディングは、強力であるとあちこち(MSも自画自賛)に書かれているが、単に文字列でプロパティ名をXAML(XMLもどき)に直接書き込んだら同期してくれるだけの仕様。しかも、C#ソースコードはコメント部分も見て変数名をリファクタリングしたら自動的に修正してくれるのに、XAMLの中は見てくれない。これだったら、正直Androidでもリフレクション使って誰でも実装できるレベルだと思うんだが…。通知もObserverのnotifyObserver辺りと殆ど同じだしなあ。
  • ただ、バインディングを通じてコントロールを触る場合は、別スレッドから実行中でも自動的にUIスレッドで実行してくれるのは割と便利。AndroidだとわざわざUIスレッドにタスク投げないといけないからね。
  • ファイル選択ボタンやフォルダ選択ボタンなど多くの画面に存在する機能や、複数の画面に渡って設置されてはいるけど同じ内容を入力させるテキストボックスなどは、フォームアプリケーションの場合、なかなか複数のコードビハインドにまたがって記述は難しかったかもしれないが、MVVMの場合は、その機能別にVMを作って、DataContextにくっつけられる。再利用性やDRY原則には、WPFの方が向いている気がする。
  • publicなメンバー変数ではなくてプロパティでないとバインディングできない。実際に書いていると、自動実装プロパティでは対応できないケースも多いので、その辺りでかなり手間がかかることも多いと感じた。まあ俺の職場のVSがExpressなんだけど・・・。
  • イベント駆動型を用いず、MVVMだけで全てを書くのは時には難しく生産性も落ちる。特にDataGridなんかは、イベントだと簡単に引数から列と行が取得できるんだけど、同じ事をViewの参照すら持たないViewModelでやると一気に難易度が上がる。つか俺には無理だった。
  • また、XAMLとViewModelだけで全ての描画処理を行うのも困難。DataGridのセルを直接弄りたい場合は、C#で直接叩いた方が効率性が上がる。その場合、ViewModelにView(Window)の参照変数を持たせないといけない。疎結合なんて夢のまた夢。
  • いまのところ、NET環境でMVVMで生産性上がるかというと、いまいち確信が持てない。C#のイベント駆動式は完成度が高いし、それに.NETはpartialクラスがあるので、XAMLに書いたコントロールの参照変数を起動時に全て用意してくれる。Androidはその辺りを自分でやることになるから、MVVMやMVPMの有用性も見出だせられるんだが。
  • AndroidXMLのレイアウトも相当難しかったが、XAMLも非常に敷居が高いつーか訳が分からん。Androidなら今ならテキストも参考文献も相当あるのだが、XAMLは海外サイト頼りになってしまう。日本でのWPFSilverlightの将来性は非常に不安。
  • ここからは言語の話。全体的にJavaC#4.0~5.0では相当の機能差がある訳だが、実際に開発していて頻繁に用いるJavaになくてC#にあるものと言えば、ディレクティブ、delegateとラムダ式と、匿名型の型推論、インデクサ辺りか。逆にJavaの利点ってEnumぐらいだよなあ。
  • Javaのプリミティブ型は、C言語か何かで実装してるのか知らんが、要はオブジェクトですらない。しかしC#はObjectから派生した構造体なので、オブジェクトでいて、それでいて値型だからプリミティブ型と同じパフォーマンス(多分)で使える。
  • オーバーライドの仕様もC#が優れてるが、インターフェイスのメソッドはoverrideを書けない。Javaは一応途中からこの問題には対処して@Overrideアノテーションをつけられるようになったが、C#は、インターフェイス名.メソッドという書き方しかできない。
  • C#のswitchはスループットできないのにbreak;必須という超糞仕様になっている。言語的には非常に洗練されているだけに、ここだけが信じられないぐらい酷い状態。三項演算子を使えということなんだろうか。
  • WPFの本は今では書店で探すのが困難だが、Androidのは腐るほどある。でもこんだけWPFではMVVMとかが提唱されていたのに、AndroidでのMVCやMVPMは局所的な支持しか得られてないっぽい辺りが意味深。


たまに、MVVM(MVPM)に対して、「画面のオブジェクトのレンダリング情報を保管する必要があるからそれを用意するという、ただ自然の手法がMVVM」という説明がされる。これは確かに事実ではあるんだけど、一方でそんなグラフィック重視ではないシステムやアプリケーションの場合、レンダリングについては「状態」ではなくて「手続き」で管理できる場合が多い。

つまり、「○○のボタンを押したら、色が■■になる。この状態で△△をしたら、色が@@になる」みたいな形で、オブジェクトのレンダリングが決まるのであれば、それはVM内でもコマンドで決まる要素である。モデルのプロパティーをラップしたVMのset()だけでレンダリングロジックが決まらないんだったら、それはもうイベント駆動で管理しているのと大差がないだろう。

俺が今進めているMVPMは、こうした手続き型+レンダリング情報保全+MVCで作っている。表向きは、Model→ViewまたはModel→ ViewModel→Modelの「通知」を採用するMVC(MVP、MVVM、MVPM)パターンにおいては異色ではあるけれど。