諸々

土曜日から気管にくる風邪を患ってしまい、気管支炎でおとなしくしてます。

Genericsについてまとめたエントリでも書こうかなと思ってJavaの入門書をいくつか流し読みしてみたのですが、なんかGenericsをみんないっせいにスルーしてますね…。やっぱり敬遠されてるのでしょうか。
そういう入門書とか、雑誌で特集組んでたやつがあれば教えてください。

あと、ブコメで無限型についてちょっと触れられていたので、その部分をJava言語仕様第3版から抜粋。このセクションの終わりのほうです。

It is possible that the process above yields an infinite type. This is permissible, and Java compilers must recognize such situations and represent them appropriately using cyclic data structures.

                                                      • -

Discussion
The possibility of an infinite type stems from the recursive calls to lub().
Readers familiar with recursive types should note that an infinite type is not the same as a recursive type.

15.12.2.7 Inferring Type Arguments Based on Actual Arguments

で、

<T extends Number & Comparable<? extends T>>

を無限型と呼ぶかどうかはちょっと不明です。Tはあくまで型変数で、その右側はただの型境界なので。

この前のエントリでは、

lub(Integer, Long) as R = Number & Comparable<? extends R>

というようなRを無限型と呼んでおり、そのエミュレーションに先ほどの型境界つきの型変数を利用していた感じです。こっちは、Rそのものの定義にRが出現しちゃいます。

型推論の正解が分からない

自分めも。だけどわかる人教えてください。

public static void main(String...args) {
   Integer a = 1;
   Long b = 2;
   Object result = method(a, b); // resultの型は?
}
static <T> T method(T a, T b) {
   return null;
}

このmethod(a, b)の型推論で、Tはどのような型に算出されるかという問題。
15.12.2.8 Inferring Unresolved Type Argumentsを追いかけながら進んでいく感じ。ただし、型推論の後半部分だけ。

続きを読む

オーバーロードの仕組みをサンプルで

オーバーライドの仕組みをサンプルで - しげるメモでオーバーライドをやったので、今回はオーバーロードについて自作してみます。
(一応)オーバーロードの仕組みを説明しておくと、Javaでは同じ名前の引数が異なるメソッドを同じクラスに複数定義できます。
以下、Overload.javaといういろいろなところで再利用するつもりのサンプルです。

public class Overload {
    public static void main(String[] args) {
        Overload object = new Overload();
        object.greetings();
        object.greetings("Overload!");
        return;
    }
    public void greetings() {
        System.out.println("Hello");
    }
    public void greetings(String message) {
        System.out.println(message);
    }
}

引数なしのgreetingsと、Stringを引数にとるgreetingsがオーバーロードで定義されています。これを実行すると、ちゃんと下のように表示されます。

Hello
Overload!

ちょっと前に「Javaオーバーロード解決は静的に行われる」と書きましたが、今回はこれをもう少し深く追っていく感じで。

続きを読む

SunのコンパイラとEclipse JDTで動きが違う(3)

酒の肴くらいにしかならない豆知識。メソッド呼び出し時の型推論とか - しげるメモの知識が前提のネタです。

下記のコードはjavacとJDTで動きが違います。

public class Assignable {
    public static void main(String[] args) throws Throwable {
        throw get(); // ここの評価が違う
    }
    static <T> T get() {
        return null;
    }
}

javacだとコンパイルエラーにはならず、JDTだとコンパイルエラーになる感じ。

...
  ThrowStatement:
    throw Expression ;

A throw statement can throw an exception type E iff the static type of the throw expression is E or a subtype of E, or the thrown expression can throw E.

The Expression in a throw statement must denote a variable or value of a reference type which is assignable (§5.2) to the type Throwable, or a compile-time error occurs.
...

14.18 The throw Statement

「the static type of the throw expression」の解釈が難しいところですが、「type which is assignable to the type Throwable」とあるので、throwの右側にある式は「Throwableに対する代入変換コンテキスト」であると推測できます。
前回の話とつなげると、「代入変換コンテキスト」であり、「メソッドの戻り値型に型推論されていない型変数が含まれる」場合、メソッドの戻り値型を代入変換コンテキストの代入先の型に代入可能であるという制約を型推論に導入します。
ExpressionはThrowableに対する代入変換コンテキストであるため、T get()のTはThrowableに代入可能であるように推論されるので、T->Throwableになるのが正解。今回はjavacのほうが正しそうです。

心の底からどうでもいいと思った。