Home

sinsengumi血風録

なぜ Java の配列は共変で、Generics は共変ではないのか

まず、Java の配列がタイプセーフではない話。

public class ExampleArray {

    public static void main(String[] args) {
        String[] strArray = {"test1", "test2"};
        Object[] objArray = strArray; // 配列は共変なので代入可能

        objArray[0] = new Integer(3); // java.lang.ArrayStoreException
    }
}

上記のように、Java の配列は共変という性質を持っているので、Object[] に String[] を代入することができます。つまり、String[] は Object[] のサブクラスである、ということです。
しかし、Generics の場合は、この性質が当てはまりません(Generics は共変ではない)

public class ExampleGenerics {

    public static void main(String[] args) {
        List<String> strList = new ArrayList<String>();
        strList.add("test1");
        strList.add("test2");

        List<Object> objList = new ArrayList<Object>();

        objList = strList; // コンパイルエラー
    }
}

なぜ、配列は共変で、Generics は共変ではないのか?(一緒の方が分かりやすいのに)

その理由の前に、
配列の例で見たように、型の混入現象は、強い型付け言語である Java としては避けたい現象です。
配列の場合は、不正な型が混入した場所で例外(java.lang.ArrayStoreException)を投げてくれます。
(これは、結構大事な所で、使用時(get)ではなく、設定時(set)にきちんと例外を投げてくれると、バグの混入場所が特定しやすいです)
なぜこんなことができるかというと、配列は自分が何の型であるかを自身で知っている(バイトコードに型情報が存在する)ので、違う型を入れたときに、自分と違うということが判定できるのです。

一方、Generics の場合はコンパイル時に型消去という操作が行われます。
型消去については、別エントリーでも書いてます(Generics(Java)の型消去について

Generics のコードは、コンパイル後にはその型情報を一切残していないので、java.lang.ArrayStoreException のような例外を投げることができません。
そのため、共変ではなくして、型の混入を防いでいるのではないかと思います。

Generics で共変っぽいことをしたい場合は、extends とかの境界条件をつければ可能です。

JPA Hibernate (H2 database) をやってみる

最近、Play framework が面白いなぁと思ってちょろちょろ遊んでたりするんですが、Play はモデル層に JPA を採用していて、実は JPA って一度も触った事が無かったのでこれを機にどういうものなのか試して見ました。

プロジェクトの準備

JPA は昔は Java EE(EJB) でしか使えなかったらしいですが、スタンドアロンでも使えるようになったらしいので、スタンドアロンで試してみます。

JPA の実装は Hibernate を使用します。
この Hibernate がやたら依存モジュールが多いので、簡単のため maven プロジェクトにします。

mvn archetype:create -DgroupId=com.example -DartifactId=JPASample

pom.xml に以下を書いておく。
pom.xml に追加する jar はここらへんを参考にしました。注意点としては、JPA を使用するには、hibernate-entitymanager というモジュールが必要なのでそれを必ず追加しておくこと。
あとは、永続化先に使用する DB の JDBC も忘れずに追加しておくこと(今回は H2 database を使用)

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>JPASample2</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>JPASample2</name>
    <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>3.6.8.Final</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>3.6.8.Final</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.6.4</version>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.3.162</version>
        </dependency>
    </dependencies>
</project>

できたプロジェクトを Eclipse にインポートさせるため、以下のコマンドを叩いて Eclipse プロジェクトにしておく。

cd JPASample
mvn eclipse:eclipse

できたら、Eclipse からインポートする。

データベース の準備

永続先として今回は H2 database を使用。Jar ファイル1つで完結するのでとても簡単。
ここから Jar を DL して Jar 実行。

java -jar h2-1.3.162.jar

Web コンソールが立ち上がると思うので、ログイン(ID、パスワードはそのままでOK)して、テーブルを作成しておく。

CREATE TABLE EMPLOYEE(ID INT PRIMARY KEY, NAME VARCHAR(255));

JPA の準備

まずテーブルにマッピングさせるエンティティクラスを作成する。

@Entity
public class Employee {

    @Id
    public int id;
    public String name;

    public Employee() {
    }

    public Employee(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

次に、JPA が DB の設定などを行う persistence.xml を作成する。
配置場所はクラスパスが通っている場所に配置する(今回は src/main/resources/META-INF/persistence.xml に配置した)
Eclipse でちゃんとクラスパス設定を行うこと。

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0"
    xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">

    <persistence-unit name="sample" transaction-type="RESOURCE_LOCAL">
        <class>com.example.Employee</class>
        <properties>
            <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" />
            <property name="hibernate.connection.driver_class" value="org.h2.Driver" />
            <property name="hibernate.connection.username" value="sa" />
            <property name="hibernate.connection.password" value="" />
            <property name="hibernate.connection.url" value="jdbc:h2:tcp://localhost:9092/demo" />
            <property name="hibernate.max_fetch_depth" value="3" />
        </properties>
    </persistence-unit>
</persistence>

実行

あとは、JPA を使って実行するのみ。

public class App {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("sample");
        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction();
        tx.begin();

        try {
            em.persist(new Employee(1, "近藤 勇"));
            em.persist(new Employee(2, "土方 歳三"));

            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close();
            emf.close();
        }
    }
}

H2 database にデータがきちんと登録されていれば成功。

Generics(Java)の型消去について

Java の Generics の実装方式の型消去についてちょっと調べました。

そもそも ジェネリックプログラミングというのは、Java だけにあるものではなく色んな言語に同様の機能があるようです。
ジェネリックプログラミング wikipedia

Java の場合、言語仕様的に、どうやって Generics を実装しているかというと「型消去(type erasure)」によって行われている。
型消去とは、「コンパイル後のバイトコードに型情報を残さない」ということ。

具体的なサンプルコードで見てみる。

public void method15() {
    List<String> list = new ArrayList<String>();
    list.add("string");
    String str = list.get(0);
}

public void method14() {
     List list = new ArrayList();
     list.add("string");
     String str = (String) list.get(0);
}

これを、jad したものが以下。

  public void method15();
     0  new java.util.ArrayList [15]
     3  dup
     4  invokespecial java.util.ArrayList() [17]
     7  astore_1 [list]
     8  aload_1 [list]
     9  ldc <String "string"> [18]
    11  invokeinterface java.util.List.add(java.lang.Object) : boolean [20] [nargs: 2]
    16  pop
    17  aload_1 [list]
    18  iconst_0
    19  invokeinterface java.util.List.get(int) : java.lang.Object [26] [nargs: 2]
    24  checkcast java.lang.String [30]
    27  astore_2 [str]
    28  return

  public void method14();
     0  new java.util.ArrayList [15]
     3  dup
     4  invokespecial java.util.ArrayList() [17]
     7  astore_1 [list]
     8  aload_1 [list]
     9  ldc <String "string"> [18]
    11  invokeinterface java.util.List.add(java.lang.Object) : boolean [20] [nargs: 2]
    16  pop
    17  aload_1 [list]
    18  iconst_0
    19  invokeinterface java.util.List.get(int) : java.lang.Object [26] [nargs: 2]
    24  checkcast java.lang.String [30]
    27  astore_2 [str]
    28  return

確かに、コンパイルされると型情報が消去されて、Java 1.4 時代のものと同じになっている。

なぜ、Java では型消去という実装方式を採用したか?

C++ の場合、同様の機能にテンプレートというものがあるらしく、そちらはコンパイル後も型情報をインライン展開して残すらしいです。
で、Java の場合、なぜ型消去という実装方式を採用したかというと、「後方互換性」のためらしい。

先の例で見たように、コンパイル後のコードが全く同じになるので、1.4 以前で書かれたコードと 1.5 以上で書かれてコードがコードが混在していても実行できる、ということ。

なるほど、よく出来てる。

Servlet 3.0 のアノテーションでは Filter の順番を指定できない?

Servlet 3.0 になってから HttpServlet クラスの @WebServlet 同様に、Filter クラスも @WebFilter というアノテーションが指定できるようになっていて、やたら便利です。

@WebFilter(dispatcherTypes = { DispatcherType.REQUEST }, urlPatterns = { "/*" },
        initParams = { @WebInitParam(name = "encoding", value = "UTF-8") })
public class EncodingFilter implements Filter {

    private String encoding = null;

    @Override
    public void init(FilterConfig fConfig) throws ServletException {
        encoding = fConfig.getInitParameter("encoding");
    }

    @Override
    public void destroy() {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        request.setCharacterEncoding(encoding);
        response.setCharacterEncoding(encoding);
        chain.doFilter(request, response);
    }
}

が、どうもアノテーションだけでは、Filter クラスの順番を定義することはできないようです。
なので、結局、web.xml に Filter を上から順番に書いていくしかないようです、残念。
(指定する方法があったら教えて欲しいです)

ちなみに @WebFilter に指定するアノテーション定義はこちら
http://docs.oracle.com/javaee/6/api/javax/servlet/annotation/WebFilter.html

やはり、順番を指定する指定は無いっぽい。

LDRich という livedoor Reader のクライアントアプリを作りました

Android で何か作りたくて、勢いで Galaxy S を買ってしまったので、アプリを作りました。

LDRich という Android アプリで、livedoor Reader のクライアントです。
ネーミングに特に意味はなくて、思いついたものは全て使われていて、ゴロで決めました。


Android 使っている人で、livedoor Reader ユーザーは使っていただけたら嬉しいです。

Android Market
使い方の解説ページ

msysGitのGit bashで日本語を使えるようになるまで

  • Posted by: sinsengumi
  • 2011年8月8日 11:34 AM
  • Git
  • |

最近、ようやくGitを使い始めました。サクサク感があっていいですね。あとGithubの存在が大きい。

で、表題なんですが、
僕はWindows派でmsysGit使ってるわけですが、デフォルト状態(Git Bash)ではコミットログの日本語が化けて出たりと何かと不便です。
そこで、いろいろ設定して日本語が表示できるようになろうという感じです。

lessとnkfをインストール

以下を参考に、lessとnkfを導入します。
http://sourceforge.jp/magazine/09/02/12/0530242/3

inputrc

set meta-flag on
set input-meta on
set output-meta on
set convert-meta off
set kanji-code utf-8

profile

export GIT_PAGER="nkf -s | less"

GIT_EDITORの設定は~/.gitconfigで行っているのでここでは設定しません。

~/.gitconfigの設定

以下のように修正。

[user]
	name = sinsengumi
	email = tomoya.sinsengumi@gmail.com
[color]
	ui = auto
[core]
	editor = 'C:/Program Files (x86)/sakura2-0-2-0/sakura.exe' -CODE=4
	autocrlf = false
[alias]
	ci = commit
	co = checkout
	st = status
	di = diff
	sh = show
	br = branch
[i18n]
	commitencoding = UTF-8

editorの部分ではサクラエディタを起動してますが、「-CODE=4」はファイルを開く時にUTF-8で開くという起動オプションです。

ちなみにこの設定はGithubに上げてみました^^;
https://github.com/sinsengumi/Configs

参考

WTPのTomcat7.0でDataSource(PostgreSQL)からConnectionを取得する

コネクションプーリングをするために、DataSourceからコネクションを取得しようと思ったら、かなりハマってしまったので、備忘録です。

手順は以下。PostgreSQLの例ですが、大体どのDB製品でも同じだと思います。

  1. (サーバの)server.xmlにResourceを定義
  2. (アプリケーションの)web.xmlにresource-refを定義
  3. JDBCドライバを配備
  4. コネクション取得

(サーバの)server.xmlにResourceを定義

まずserver.xmlにResourceを定義します。WTPでサーバを動かしている場合は、以下のようにServersプロジェクト内にserver.xmlがあると思います。

server.xml
server.xml

この中にアプリケーションのContextタグがあると思うので、そこにResourceを追加する。
ここが、Tomcat 6.0と記述が異なっていたので、かなりハマってしまいました。しっかりマニュアルのほうを読まないといけませんね。

server.xml

<Context docBase="Sample" path="/Sample" reloadable="true" source="org.eclipse.jst.jee.server:Sample">
	<Resource name="jdbc/postgresql" auth="Container" type="javax.sql.DataSource"
		driverClassName="org.postgresql.Driver" url="jdbc:postgresql://localhost/SampleDB" username="postgres"
		password="xxxxxx" maxActive="20" maxIdle="10" maxWait="-1" />
</Context>

(アプリケーションの)web.xmlにresource-refを定義

次にアプリケーション内のweb.xmlにresource-refを定義します。サーバのweb.xmlではないので注意。

web.xml

<resource-ref>
	<res-ref-name>jdbc/postgresql</res-ref-name>
	<res-type>javax.sql.DataSource</res-type>
	<res-auth>Container</res-auth>
</resource-ref>

JDBCドライバを配備

WTPでサーバを起動している場合は、サーバプログラム本体のcommon/libにいくらjarを置いてもクラスパスには追加されません。
WTPでjarを追加するには以下のようにします。

プロジェクト・エクスプローラーを右クリック → 実行(実行構成) → クラスパス → ユーザー・エントリでjarを追加

コネクション取得

これで準備ができたので、実際にServletからコネクションを取得してみます。

SampleServlet

package jp.co.njk.nems.controller.authentication;

import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;

@WebServlet("/SampleServlet")
public class SampleServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    private Connection connection;

    public void init(ServletConfig config) throws ServletException {
        try {
            InitialContext context = new InitialContext();
            DataSource dataSource = (DataSource) context.lookup("java:comp/env/jdbc/postgresql");
            connection = dataSource.getConnection();

        } catch (NamingException e) {
            throw new RuntimeException(e);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        System.out.println(connection);
    }
}

参考

TeedaのラジオボタンにString型ではなく列挙型(Enum)を使う

Teedaのラジオボタンは通常、String型でPageクラスに定義します。
http://teeda.seasar.org/ja/extension/reverse/input.html#%E3%83%A9%E3%82%B8%E3%82%AA%E3%83%9C%E3%82%BF%E3%83%B3_%E9%9D%99%E7%9A%84
が、そこを列挙型(Enum)することができます。こちらの方が型安全になっていいかなと思います。
もちろん、静的・動的両方いけます。

以下、サンプルです。

HTML

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:te="http://www.seasar.org/teeda/extension" xml:lang="ja"
    lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Enumのサンプル</title>
</head>
<body>
<form id="Form">
<span id="allMessages"></span>

<h1>ラジオボタン(静的)</h1>
<div id="sex">
    <label><input type="radio" name="sex" value="MALE" />男性</label>
    <label><input type="radio" name="sex" value="FEMALE" />女性</label>
</div>

<h1>ラジオボタン(動的)</h1>
<input type="hidden" id="languageItemsSave" />
<span id="language">
    <input type="radio" name="language" />
</span>

<p><input type="submit" value="送信" id="doSubmit" /></p>

</form>
</body>
</html>

列挙型

public enum Sex {
    MALE("男性"),
    FEMALE("女性");

    private String japaneseName;

    private Sex(String japaneseName) {
        this.japaneseName = japaneseName;
    }

    public String getJapaneseName() {
        return japaneseName;
    }
}
public enum Language {

    JAPANESE("日本語", "JAPANESE", "こんにちわ"),
    ENGLISH("英語", "ENGLISH", "Hello");

    private String label;
    private String value;
    private String greeting;

    private Language(String label, String value, String greeting) {
        this.label = label;
        this.value = value;
        this.greeting = greeting;
    }

    public String getLabel() {
        return label;
    }

    public String getValue() {
        return value;
    }

    public String getGreeting() {
        return greeting;
    }
}

Pageクラス

public class EnumSamplePage {

    public Sex sex;

    public Language language;
    public List<Language> languageItems;

    public Class<?> initialize() {
        // 初期値の設定
        sex = Sex.FEMALE;
        language = Language.ENGLISH;

        languageItems = new ArrayList<Language>();
        for (Language lang : Language.values()) {
            languageItems.add(lang);
        }

        return null;
    }

    public Class<?> prerender() {
        return null;
    }

    public Class<?> doSubmit() {
        System.out.println(sex.getJapaneseName());
        System.out.println(language.getGreeting());
        return null;
    }
}

注意としては、
静的なラジオボタンで使う場合は、value属性に“列挙子”を指定すること(サンプルだと「MALE」「FEMALE」)
静的なラジオボタンで使う場合は、列挙型のフィールドに“label”“value”を変数名にもつ値を定義すること。
あと、HTMLにhidden属性に”~~ItemsSave”を配置すること。これをしないとPOSTしたときにデータが消えてしまう(これはString型でも同じですが)

livedoor Reader をJavaから操作する何か作った

  • Posted by: sinsengumi
  • 2011年2月21日 12:21 AM
  • Java
  • |

追記
Github にホスティングしました。これからはGithub 使います。
https://github.com/sinsengumi/LDR4J

掲題の通り、livedoor Reader をJavaからHTTPアクセスして未読記事取ったり、ピン付けたりするライブラリ「ldr4j」を作りました。
実はAndroidでlivedoor Readerクライアントアプリを作りたくて、その前段階としてこのラッパーライブラリを作った感じです。

で、生まれてはじめて、まとまったコードをOSSとして公開してみました。
コードはSourceForge.JPでホスティングしています。
(eclipseプラグインのEGITがよさそうであれば、githubでホスティングしたいと思います。)

「ldr4j」プロジェクトページ

http://sourceforge.jp/projects/ldr4j/

以下で、Subversionからチェックアウトできます。
http://svn.sourceforge.jp/svnroot/ldr4j/

※なお、livedoor Reader API はlivedoor から正式にアナウンスされているAPIではないので、仕様変更で使えなくなる可能性はあります。

livedoor Reader が大好きで、もうこれがないとネット生活していけないくらいですね。
今回 ldr4j を作っていく過程で、livedoor Reader がどういう風に出来ているかもなんとなくですがわかりました(livedoor ReaderのOSS版である fastladder を参考にしました。)

このライブラリはこれからもちゃんとメンテナンスしていこー。テストとかもちゃんと書きたいし。

参考

よく見るClosable使ったコード

  • Posted by: sinsengumi
  • 2011年2月16日 8:23 PM
  • Java

Streamなんかを閉じる(close)時のコードなんかでよく見るパターン。
nullチェックして、closeする。さらにそのcloseはチェック例外出すからめんどいよ、ってことなのかしら。
結構有名なのかな?

private void close(Closeable stream) {
    if (stream != null) {
        try {
            stream.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

Home

Search
Feeds
Meta

Return to page top