java:walkFileTreeメソッドの使い方

walkFileTreeメソッドとは、ディレクトリ構造を再帰的に走査するメソッドです。
Java7で追加されたFilesクラスが提供するメソッドの一つであり、比較的新しいメソッドです。
(このメソッドの提供により、ディレクトリ構造については自力でCompositeパターンを組む必要がなくなりました)

walkFileTreeメソッドの引数は2つあり、1つ目は起点となるパス、2つ目はFileVisitorインターフェースです。
FileVisitorインターフェースにはディレクトリ構造を走査した際の処理が定義されており、FileVisitorインターフェースを利用者が実装することでディレクトリ走査時の具体的な処理内容を記述可能になります。

以下、サンプルコードです。
詳しい使い方はWebを調べると出てきますので、必要に応じて調べてみると良いでしょう。

【サンプルコード】

・FileVisitorTest.java

・WalkFileTreeTestMain.java

【テスト用ディレクトリ構造】

【実行結果】


いかがでしたでしょうか?

Java7がリリースされてからもう10年近く経ちますが、Javaを昔に勉強した人だとJava7(やJava8)の知識が抜けていることが意外と少なくないと思います。
しかし、Java7では、今回紹介したwalkFileTreeメソッドのように便利なFilesクラスが提供されています。
勉強し直すのは面倒かもしれませんが、新機能を使うことによりコードを簡潔に、かつ安全にすることができるので、勉強する価値はあると思います。

これからも、JavaやC#等で提供されている便利な文法を紹介していきたいと思います!

C言語にはないC++独自の文法の簡単な列挙

C++はC言語を拡張して開発された言語であり、C++ではオブジェクト指向をサポートする文法が追加されています。
基本的にC言語で使用していた文法はC++でも使用できるので、学習という面で見るとC言語を扱える方であればC++で追加された文法を覚えればC++も扱えるようになります。

今回の記事では、C++で追加された文法の中から、主なものを簡単に列挙していきます。

なお、javaやC#といったオブジェクト指向言語も扱えるのであれば、オブジェクト指向の概念は理解しているはずなので、追加された文法の理解も早いと思います。
javaは基本情報技術者試験で出題される言語、C#はC++と同じく.NET Frameworkでサポートされる言語であり、C++を学ぼうとする方はjava・C#についてある程度の知識があることが多いと思うので、javaやC#とも簡単に対比します。

・クラスの概念の追加

C++では、”class クラス名”でクラスを定義することができます。
javaやC#ではお馴染みの、
 アクセス識別子
 コンストラクタ
 継承
 抽象クラス
 static
といった機能が使用できます。
C#で実装されているデストラクタ(オブジェクト破棄時の後処理)もC++で使用可能です。

javaやC#とは記述方法が若干異なりますが、javaやC#の経験があれば戸惑うことは少ないと思います。
ただし、newで確保した領域を意図的にdeleteで破棄する必要がある、ということには注意する必要があります。
忘れるとメモリリークになります。
(javaやC#のようなガベージコレクションの仕組みは無い)
newはC言語で言うmalloc、deleteはC言語で言うfreeに似ていますが、new/deleteの場合はコンストラクタ/デストラクタが呼ばれるという違いがあります。

なお、インターフェースの機能は使用できませんが、書き方次第でインターフェースに近いことはできます。

・参照の概念の追加

クラスの概念とも関連があるのですが、javaやC#ではお馴染みの参照型変数の概念が追加されています。

参照型変数の中身はポインタで、C言語ではお馴染みのポインタ型変数とその点では同じなのですが、参照型変数の場合は指し示すアドレスを自由に変更できない(インクリメント等ができない)という違いがあります。
この違いにより、ポインタ型変数で犯しがちなミスを減らす効果があります。

・スコープ解決演算子の追加

C++では、変数名やメソッド名の衝突を避けるため、スコープ解決演算子(::)を使用します。

例えば、”std::cout”と書いた場合、stdという名前空間(クラス名)のcout(main関数実行時にシステムにより生成されるオブジェクト名(変数名))という意味になります。
また、外部の自作クラスにアクセスするような場合も、このスコープ解決演算子を使用します。

なお、スコープ解決演算子は、ソースコードの冒頭で using namespace 名前空間;のようにデフォルトの名前空間を指定することで省略可能です。

・ストリームの概念の追加

“<<“で出力ストリーム、”>>”で入力ストリーム、という意味となります。
(”<<“はostreamクラス、”>>”はistreamクラスから派生させることで使用できます)

例えば、
“std::cout << “Hello World!\n”;”
と書けば標準出力に”Hello World!”と出力できますし、
“std::cin >> hoge;”
と書けば標準入力を変数”hoge”に渡すことができます。

・string型の追加

C言語で文字列操作を行う場合はchar型のポインタを使う必要がありましたが、C++ではjavaやC#と同じように用意に文字列操作を行うためにstring型が用意されています。
string型のc_str()関数を用いれば文字列の先頭のポインタを取得することもできるので、C言語の従来の文字列操作用の関数も併用できます。

・bool型の追加

意外にもC言語にはtrue/falseを保持するbool型が用意されていません。
C++には、java(boolean型)やC#のようにbool型が用意されています。

・関数のオーバーロードの追加

同じ名前の関数でも、関数の引数が異なれば複数定義できます。
これはjavaやC#ではお馴染みの機能です。

・例外制御の追加

C++ではtry~catch構文が用意されています。
throwで意図的に例外を投げることもできます。
これもjavaやC#ではお馴染みの機能です。
ただし、finallyは用意されていません。
finally句で行うようなリソースの解放を行いたい場合は、前述のデストラクタや、デストラクタで自動的に領域を解放するオブジェクト(スマートポインタ)を利用する必要があります。


いかがでしたでしょうか。

個人的には、C++はスコープ解決演算子やストリームの概念で戸惑いました。
しかし、簡単な例で学んでいけばスコープ解決演算子やストリームの概念を理解するのは難しくないですし、他の文法はC言語やjava・C#に近いので、これらの言語を学んでいる方ならすんなり理解できると思います。
基本情報技術者試験で出題されるC言語やjavaに比べると、これまで紹介してきたC#や今回紹介したC++は学んでいる方は少ないと思いますが、これらの言語は文法的には近いものがあるので、ある日突然必要になったとしても習得は難しくないと思います。

では、また次回!

C#:デリゲートの説明(Action・Func、マルチキャストデリゲート含む)

デリゲートとは、一言で言えば「関数を変数として扱う」機能です。
個人的には、「1つの関数のみを定義したクラスやインターフェースのようなもの」と捉えた方が分かりやすいと思います。

実務ではコールバック(本処理の終わりに特定の終了処理をさせる)をさせたい場合に使用されることが多いです。

以下はサンプルコードです。
理解を助けるためにコメントを入れているので、それもご参照ください。

【サンプルコード1】

【実行結果1】


引き渡す関数は匿名メソッドやラムダ式でも記述可能であり、これらを利用することで関数の定義を省略することができます。

また、引き渡された関数を受け取る際、.Netで用意されている以下のジェネリック定義を使用することができます。
ジェネリック定義を使用することで、デリゲートを省略することができます。

・System.Action型

戻り値がない場合に使用
Action、Action<引数1>、Action<引数1,引数2>…等

・System.Func型

戻り値がある場合に使用
Func<戻り値>、Func<引数1,戻り値>、Func<引数1,引数2,戻り値>…等

以下は、ラムダ式とSystem.Func型を使用し、サンプルコード1を簡略化する例です。

【サンプルコード2】

【実行結果2】


引き渡す関数は、+演算子で追加したり-演算子で削除したりすることもできます。
(マルチキャストデリゲート)

以下、+演算子や-演算子を試してみた例です。

【サンプルコード3】

【実行結果3】


いかがでしたでしょうか?

デリゲードはjava経験者にとっては馴染みのない文法なので、記事にしてみました。
繰り返しになりますが、「1つの関数のみを定義したクラスやインターフェースのようなもの」として捉えると、理解しやすいと思っています。

これからも、C#ならではの文法を紹介していきたいと思います!

C#:数値リテラルの一覧

javaでは数値リテラルの扱いに注意が必要になることがあります。
C#の仕様も気になったので、一覧にまとめてみました。

javaとの違いは以下の通りです。

・整数型に符号無し(u)、実数型にdecimal型(m)も指定できる

 ※decimal型は丸め誤差が発生しない実数型、javaで言うBigDecimal

・整数型の場合、精度や符号有無を自動判定する

なお、以下のように、接尾辞・小数点無しの数値同士の計算結果を実数型の変数に格納する場合に、整数型として計算されてしまい小数点以下で切り捨てられてしまうのはjavaと同じなので、注意が必要です。

【サンプルコード】

・Program.cs

【実行結果】


いかがでしたでしょうか?

数値リテラルの扱いを見ても、javaに近いことがわかります。
基本的には、javaの仕様を拡張したものであると考えて良いでしょう。

今回の記事とは直接関係ありませんが、decimal型がサポートされているのはありがたいです。
(javaのBigDecimal型は扱いが難しく、バグも発生しやすかったので)

これからも、C#ならではの文法を紹介していきたいと思います!

C#:オブジェクトの中身をコピーする方法(MemberwiseCloneメソッド実装)

参照型変数(主にクラスのオブジェクト)をコピーする場合、単純に「=」で代入するだけでは不十分な場合があります。
参照型変数の中身は参照先(オブジェクトのメモリ領域を示すポインタ)です。
「=」で代入するだけでは、参照先だけがコピーされて、参照しているものは同じという状態になるので、コピー先の変更がコピー元に影響してしまいますし、その逆にコピー元の変更がコピー先に影響してしまいます。

これを避けたい場合は、MemberwiseCloneメソッドを用いて中身を丸ごとコピー(新たにメモリ領域を確保し書き込み、ディープコピー)する必要があります。
MemberwiseCloneメソッドはobject型で定義されており、C#ではすべての型はobject型から派生しているので、特別な記述を行わなくともMemberwiseCloneメソッドを使用できます。ただし、戻り値はobject型なので、キャスト等の考慮は必要です。
(javaの場合はこちらの記事のようにインターフェースの実装が必要だったり例外処理が必要だったりと色々面倒です。後発言語であるC#では言語仕様上ディープコピーを始めから考慮している印象を受けます。)

以下、サンプルコードです。
参照先のみコピーした場合とMemberwiseCloneメソッドで中身をコピーした場合を比較しています。
参照先のみコピーした場合は、コピー後にコピー先を変更した際にコピー元が影響を受けていますが、MemberwiseCloneメソッドで中身をコピーした場合は影響を受けていません。

【サンプルコード】

・CloneableItem.cs

・ItemCloneMain.cs

【実行結果】


いかがでしたでしょうか?

C#は後発言語であるため、記述を簡略化できる場面が多いです。
今回紹介したディープコピーの例もそうですし、getter・setterの記法もC#だと簡略化できます(今回のサンプルコード中にも出てきます)。
javaを触った後にC#を触ると、こうした細かい所で便利さを感じます。

これからも、C#に関する記事を投稿していきたいと思います!