Java8の日付・時刻クラス

現在のJavaで日付を扱うには、java.util.Dateクラスかjava.util.Calendarクラスまたは両方を使わないといけない。

これが結構面倒で、どうして.NETのように簡単に扱えないのか非常に不満であった。

Java8ではこれらを置き換えるため、新たに、java.timeパッケージが導入されるようである。Javadocを見る限りでは、DateやCalendarよりは使いやすいように思える。が、未だに日時どうしの演算ができないのはなぜだろうか?

.NETでは日付型(DateTime)どうしで、減算を行うと、TimeSpan(相対時間)型を得ることができるし、DateTimeにTimeSpanを加減算することも可能である。

もちろん、Javaでは演算子のオーバーロードができないことは承知しているが、メソッドとして用意しても良いのではないか?確かに、addDays,addHoursやminusDays,minusHours等のメソッドはあるが、単純な(日時型どうしの)減算位できても良いのではないだろうか?

まぁ、無いものは仕方ないので、とりあえず、新しいLocalDateTimeクラスの使用例を挙げておく。

↑DurationやPeriodクラスのstaticメソッドを使うと計算できる模様。

import java.time.*;
import java.time.format.*;
class localdt {
 public static void main(String[] args) {
  // 現在日時を取得
  LocalDateTime dt = LocalDateTime.now();
  // フォーマットを指定して文字列化
  System.out.printf("現在時刻は%sです。\n",
    dt.toString(
    dt.format(
    DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss")));
  // 2012/2月の最後の日は?
  LocalDate dt2 =
    LocalDate.of(2012,3,1);    // 年月日指定でLocalDateを得る
  dt2 = dt2.minusDays(1);        // 1日マイナスする
  System.out.printf("2012年2月の最後の日は%sです\n",
    dt2.toString(
    dt2.format(
      DateTimeFormatter.ofPattern("yyyy/MM/dd")));
  // 1964/02/03生まれの人の年齢は?
  LocalDate now = LocalDate.now();
  // 今度は文字列からparseしてみよう
  LocalDate birth = LocalDate.parse(
    "1964/02/03",
    DateTimeFormatter.ofPattern("yyyy/MM/dd"));
  // 年月日を取り出すメソッドを使用する。
  // この辺りはやはりpropertyを実装しているC#とかに較べると
  // コードがだっさー
  int age = now.getYear() - birth.getYear();
  int nm = now.getMonthValue();
  int bm = birth.getMonthValue();
  int nd = now.getDayOfMonth();
  int bd = now.getDayOfMonth();
  age -= (nm < bm ? 1
    : (nm == bm && nd < bd ) ? 1 : 0);
  // これで計算できるそうです。(実際試したらできました)
  int age = Period.between(birth,now).getYar();
  System.out.printf("1964/02/03生まれの人は%d才だね\n",age);
 }
}

実行結果

Java8 LocalDateTime実行結果

Java8 LocalDateTime実行結果

カテゴリー: Java, 技術情報 | 2件のコメント

Java8ラムダ式の問題点

Java8のラムダ式の実装には大きな問題があると思う。

まだ、正式に提供されているわけではないので、まだ分らないが正式提供版では是非とも直してもらいたい点が1つ。

それは、ラムダ式の中でラムダ式外の変数をキャプチャできないこと。キャプチャだけならともかく、参照すらNG(finalまたはそれに準ずるもの以外)。

これは、ループ内の変数等をラムダ式で扱えないことを意味する。例えば、以下のようなものはコンパイルエラーとなってしまうのである。

for (int i=1; i <= 12; i++) {
persons.stream()
.filter(p->p.getBirthMonth() == i) // ここでiがfinalじゃないって怒られる
.forEach(p->{System.out.println(p);});
}

これって、超使えないと思わない?

カテゴリー: Java, 技術情報 | 2件のコメント

Windows8からの投稿

Windows8のStoreにある、Blog Writerには使えそうなものが無いので、とりあえずWordを使ってみる。

Wordなら、図とかも挿入できるはずだよねぇ。

ちなみに、↑はONKYOのタブレット+Microsoft Wedge Mobile Keyboard(カバーがタブレットスタンドになるやつ)

では、この状態で投稿してみよう。

カテゴリー: Windows8, テスト | 1件のコメント

Java8のlambdaとStream

Java8のlambdaの姿がだんだん見えてきた。http://cr.openjdk.java.net/~briangoetz/lambda/sotc3.htmlによるとJava8ではlambda式を以下のように定義する。

[(][引数1[,引数2…引数n][)]-> [{] 式1[;式2…式n;return 式] [}]
引数が1つの場合は()を省略可能。また式が1つの場合は{}を省略することができ、その結果が戻り値となる。複数の場合は{}でくくり、return文で値を返す。

lambda式はArrays.sort等の現行のメソッドに使用することが可能。例えば、以下のような感じ

MyClass[] ary ・・・
Arrays.sort(ary,
 (p1,p2)->p1.Age – p2.Age)
);

しかし、lambda式が一番効果を発揮するのは新たなクラスインターフェースStreamを使用する場合だろう。このクラスを用いると、インターフェースをインプリしているクラスでは.NETのLINQのようなことが可能となる。

List<Person> persons = new ArrayList<Person>() {{
  ・・・
}};
// personsリストから成年者のデータを取り出して年齢の低い順にソートして出力
persons.stream()
 .filter(p->p.getAge() >= 20)
 .sorted((p1,p2)->p1.getAge() – p2.getAge())
 .forEach(p->System.out.println(p));

// personsリストから未成年者のみのリストを作成する
List<Person> nonadults = persons.stream()
 .filter(p->p.getAge() < 20)
 .into(new ArrayList<>());

まだ、本物がリリースされていないので、何とも言えないが、Java™ Platform, Standard Edition 8 Early Access with Lambda Supportで試してみたらちゃんと動いた。これが、この仕様のままリリースされるなら、結構使い勝手が良いのではないだろうか?

カテゴリー: Java, 技術情報 | コメントする

VC++/CLI

仕事の関係で、C言語で作られたDLLを.NETで使うことになり、Wrapperを作るのに何がいいかと探していたら、VC++/CLIに行き着いた。

VC++/CLIでは、Native C/C++のリソースへのアクセスも.NET Managedリソースへのアクセスも簡単にできる。

Native C/C++へは普通にアクセスできる。Managedリソースは基本的に参照型なので、ポインタ扱いとなるが、宣言は*ではなく、^を用いる。また、インスタンスの作成はnewではなく、gcnewを用いる。以下のような感じ。

String^ s;
MyClass^ c = gcnew MyClass();
List<String^>^ lst = gcnew List<String^>();

^>^って顔文字みたいだな。

また、Managedのクラスを定義する場合は、classの前にrefを付ける。クラスの定義の仕方は基本的に普通のC++と変わらない。(プロパティとか使えるけど・・・)

ref class MyClass {
public:
 property String^ Name;
    ・・・
 property int Age {
  int get() {
    ・・・
  }
  ・・・
 }
}

なお、Native DLLから関数等をインポートするにはC#等と同じく、DllImport属性を用いる。

今までほとんど気にしていなかったが、Native C/C++と.NETのWrapperを作るのに非常に適していると思う。

カテゴリー: .NETフレームワーク, 技術情報 | 2件のコメント

Windows Store App StyleのWebViewBrush

Windows Store App Style(旧称Metro Style)用のBrush(塗りつぶし用ブラシ)クラス群の中にWebViewBrushというちょっと変わったブラシがある。

このブラシはWebViewコントロールと密接に関係しており、指定したWebViewの現在の表示内容(レンダリングされたHTMLページ)を塗りつぶし背景にするブラシである。

この、現在内容というのが少々くせ者で、レンダリング中でも、その内容がそのまま、ブラシ内容となってしまうので、WebViewのレンダリングが終了した時点の内容を表示したい場合にはWebViewクラスのLoadCompletedイベントでWebViewクラスのリフレッシュを行わなくてはならない。また、表示内容はWebViewコントロールの表示幅や表示高に依存するので、注意が必要である。

コード例
XAML
・・・
<TextBox x:Name=”txtURL” ・・・ />
<Button x:Name=”btnNavigate” Click=”btnNavigate_Click”
Contents=”ページ読込” ・・・/>
<Rectangle x:Name=”RectWebImage” ・・・>
・・・
<WebView x:Name=”wvSource” Width=”640” Height=”480” Visibility=”Collapse”
LoadCompleted=”WebView_LoadCompleted” ・・・/>

C#
・・・
protected void btnNavigate_Click(object sender, RoutedEventArgs e)
{
// WebViewに指定URLレンダリング要求
wvSource.Navigate(new Uri(txtURL.Text));
}
//
//  WevViewのレンダリング完了イベント
//
protected void WevView_LoadCompleted(object sender, NavigationEventArgs e)
{
// WebViewBrushの作成
WebViewBrush br = new WebViewBrush();
// コピー元を設定
br.SetSource(wvSource);
// 再描画
br.Redraw();
// 対象のブラシとして指定
RectWebImage.Fill = br;
}

実行例

image

という感じでWebページを背景とする事が可能。

単体だとこれでうまく動くのだが、asyncとかawaitを多用したアプリを作ったら、なぜか、LoadCompletedイベントが発生してくれない。WebViewコントロールのVisibilityをVisibleにするとちゃんとWebページはレンダリングされているのだが・・・ググってみてもそれらしき現象はあまりない様子だし・・・少々、困っております。

ちなみに、作っているのはRSSリーダ。GridViewを使ってそれ(旧称Metro UI)っぽく作っているのだけど、画像部分って、記事のキャプチャ画面ぐらいしか無いもんね。

カテゴリー: .NETフレームワーク, C#, Windows8, 技術情報, 開発手法 | 1件のコメント

Entity Framework+MySQLでコードファースト

Entity Frameworkコードファースト開発をMySQLで行ってみた。

SQL Serverを使用したときと比べて結構制限があったりバグがあったり・・・

問題

  1. エンティティにstringプロパティがあった場合に、デフォルトではmediumtext型に変換されてしまい、そのままではキーとして扱う事ができない。Column(TypeName=やStringLength属しを指定してあげないと駄目なようだ。
  2. __MigrationHistoryテーブル作成例外が発生する。これはバグじゃないの?DBInitializerなどで__MigrationHistoryテーブルを作成してあげる必要がある。とりあえず、http://brice-lambson.blogspot.jp/2012/05/using-entity-framework-code-first-with.htmlからコードを丸ごとコピーして動作確認。SQLがMySQL用なので、ターゲットを別のDBエンジンにした場合を考えなければ・・・
  3. 上記に関連する事柄で、2回目以降の呼び出しで発生。まぁ、丸写しにしたのが悪いんだけど、なぜかSQLのカラム名に実際には存在しないカラム名が指定されてしまう。とりあえず、その部分をコメントアウトして動かしたけど、Migrationとかに影響でそうな気がする。
  4. リレーションで1:nのn側のデータを取得しようとすると、「1つの接続に対してDataReaderを複数開くことはできない」という例外が発生MySQL Connectorが対応してない模様。仕方ないので、AsEnumerable()でIEnumerableに変換する。でも、メモリ食いそうだな・・・

結論

SQL Serverを使用した場合と比べて、修正しなければいけない箇所が多すぎる。Entity Framework側も、MySQL Connector側も早く修正して欲しいものである。

コードファーストはSQL ServerよりMySQLの方が向いていると思うからね。

あ、ちなみに、DBの接続先は接続文字列で指定した。Entity Frameworkの一般的な方法も探したんだけど見つからなかったので・・・

<configuration>
  <connectionStrings>
    <!– コンテキストクラス名と同じ名前にするとこの接続文字列が使用される –>
    <add name=”MySQLContext” connectionString=”server=localhost;database=・・・”
         providerName=”MySql.Data.MySqlClient”/>
  </connectionStrings>
</configuration>

カテゴリー: .NETフレームワーク, 技術情報, 開発手法 | コメントする

SQLLocalDB+Entity Frameworkでコードファースト

SQL2012から提供されている、SQLLocalDBはSQL CEと違って、.NETからSqlClientでアクセスが可能なだと以前、書いた記憶がある。しかし、実際に試してみたわけでは無かったので、ちょっと試してみた。

まず、コマンドプロンプトから試験用のインスタンスを作成する。

>SQLLocalDB c Notice
LocalDB instance “Notice” created with version 11.0.

>SQLLocalDB i Notice
Name:               Notice
Version:            11.0.2318.0
Shared name:
Owner:              <オーナー情報>
Auto-create:        No
State:              Stopped
Last start time:    2012/07/31 12:38:15
Instance pipe name:

エンティティとDBコンテキストを作成

public class Notice
{
    [Key]
    public int NoticeID { get; set; }
    public string Title { get; set; }
    public string Author { get; set; }
    public string Body { get; set; }
    public DateTime Updated { get; set; }
    public virtual ICollection<Comment> Comments { get; set; }
}
public class Comment
{
    [Key]
    public int CommentID { get; set; }
    public string Author { get; set; }
    public string Body { get; set; }
    public DateTime Updated { get; set; }
    [ForeignKey(“Notice”)]
    public int NoticeID { get; set; }
    public virtual Notice Notice { get; set; }
}
public class NoticeDB : DbContext
{
    public DbSet<Notice> Notices { get; set; }
    public DbSet<Comment> Comments { get; set; }
}

構成ファイルのConnectionStringをLocalDB用に変更

<entityFramework>
  <defaultConnectionFactory type=”System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework”>
    <parameters>
      <parameter value=”server=(localdb)\Notice; Integrated Security=True” />
    </parameters>
  </defaultConnectionFactory>
</entityFramework>

プログラムを実行してNoticeDBクラスのインスタンスを作成後、テーブルにデータなどを追加してあげると・・・

image

LocalDB上にDBとテーブルが作成された。

カテゴリー: .NETフレームワーク, C#, SQL Server, 技術情報 | 1件のコメント

Windows Azureストレージへの接続(接続文字列の使用)

Windows Azureストレージへ接続する方法はいくつかあるが、構成ファイルなどに接続文字列を格納しておいて、それを使用して接続する方法が汎用性があって良いと思われるので、その方法をメモ。

接続文字列からCloudStorageAccountインスタンスを得るには、下記を用いる。

CloudStorageAccount.FromConfigurationSetting(<構成要素名>)

ただし、これをいきなり呼び出すと、System.InvalidOperationException 例外が発生する。

これに対処するには、Azureのサンプルコードにある下記のコードをそのまま、Application_Startなどにコピーする。このコードはサービス構成ファイルに変更があった場合にに、CloudStorageAccountインスタンスを更新するためのものらしい。

// This code sets up a handler to update CloudStorageAccount instances when their corresponding 
// configuration settings change in the service configuration file.
 CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) =>
{
 // Provide the configSetter with the initial value
 configSetter(RoleEnvironment.GetConfigurationSettingValue(configName));

 RoleEnvironment.Changed += (s, arg) =>
 {
  if (arg.Changes.OfType<RoleEnvironmentConfigurationSettingChange>()
   .Any((change) => (change.ConfigurationSettingName == configName)))
  {
   // The corresponding configuration setting has changed, propagate the value
   if (!configSetter(RoleEnvironment.GetConfigurationSettingValue(configName)))
   {
    // In this case, the change to the storage account credentials in the
    // service configuration is significant enough that the role needs to be 
    // recycled in order to use the latest settings. (for example, the 
    // endpoint has changed)
    RoleEnvironment.RequestRecycle();
   }
  }
 };
});

このコードを追記した後、CloudStorageAccount.FromConfigurationSettingを呼び出すことにより、CloudStorageAccountのインスタンスを使用することができる。(ストレージにアクセスできるようになる。)

ちなみに、接続文字列はRolesの下のRoleフォルダにある、Roleを開いてSettingタブにtypeをConnection Stringとして追加することで上記のメソッドで使用することができるようになる。

image

接続文字列の値は以下のようなフォーマットとなる。

DefaultEndpointsProtocol=①;AccountName=②;AccountKey=③

①には接続のプロトコルで、httpまたはhttpsを指定する。
②にはStorageAccountのアカウント名を指定する。
③にはStorageAccountのアクセスキーを指定する。

例)
DefaultEndpointsProtocol=http;AccountName=MyAccount;AccountKey=・・・

以上。

カテゴリー: Azure, C#, 技術情報 | コメントする

Windows 8 Metor Appについて

Windows 8 Consumer PreviewにVisual Studio 11 Express for Windows 8 Betaをインストールして、簡単なMetroアプリケーションをC#で作ってみた。

metroapp

UIはWPFやSilverlightと同様、XAMLを使用して作成する。XAMLコントロールはそれなりに揃っているが、TreeViewやDatePicker,Calenderは是非とも欲しいところだ。

気がついた点

使用するライブラリはSystem.で始まる通常の.NETライブラリではなく、Windows.から始まるライブラリを多用する。

ファイルの取り扱いが今までのアプリケーションとはかなり異なり、今までのライブラリをそのまま使用することができないので、かなり時間を食ってしまった。例えば、StreamReaderのコンストラクタにファイル名を使用するものが使用できなかったりする。StreamReaderクラス自体は存在するのだが、コンストラクタの第一パラメータは必ず、Streamになっている。これらのStreamを得るには、Windows.Storageネームスペースのクラス群を使用する。

IO系のライブラリメソッドはほとんど非同期となっており、シーケンシャル実行のような見た目でコーディングするには、呼び出し元のメソッドにasync,ライブラリメソッドの呼び出し時にはawaitキーワードを用いる必要がある。

例えば以下のような感じ

// HttpClient.GetStreamAsyncを見かけ上同期と同じように使用するため、async 
// キーワードをメソッドに付ける
 private async void lstFeeds_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
 HttpClient cli = new HttpClient();
 RssFeedUrl feedurl = (RssFeedUrl)lstFeeds.SelectedItem;
 // 非同期メソッドにawaitキーワードを付けると、実際にはこの時点で呼び出し側メソッド
 // を一時中断して、非同期動作が完了した後、これ以降のステートメントが実行される 
 var stm = await cli.GetStreamAsync(new Uri(feedurl.Url));
     ・・・

プログラムからIEへWebページを表示させたり、他のプロセスを起動させるには、System.Diagnostics.Process.Start()は使用できず(クラスがロードされない)、下記を使用する。(他にも、一般の.NETライブラリで使用できないものが多数ある)

Windows.System.Launcher.LaunchUriAsync(Uri)
Windows.System.Launcher.LaunchFileAsync(IStorageFile)

作成したアプリケーションを他のPCにデプロイしようと思ったのだが、パッケージを作成して、コピー後パッケージ内のバッチファイルを起動したところ、エラーとなり、インストールできない。

調べてみると、PCがActive Directoryに参加していて、グループポリシーが適用されていないと、駄目らしい。

もちろん、Storeに置くことはできるだろうが、社内用に作った物をStoreに置くわけにも行かないだろうし、この辺はもう少し何とかならないものだろうか・・・

カテゴリー: .NETフレームワーク, C#, Windows8 | コメントする