質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.48%

Comparatorインターフェイスについて

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 3
  • VIEW 2,437
退会済みユーザー

退会済みユーザー

Comparatorを生成するstaticメソッドやdefaultメソッドの使用方法で、Comparatorのメソッドを続けて呼び出す際に(なんか用語ありましたっけ?)、型を指定しないとコンパイルエラーとなってしまう原因が分かりません。

List<Element>をCollections.sortを使ってn1>n2>n3の昇順に並び替える

  1. Elementクラス(getterは省略してます)
class Element {
    int n1;
    int n2;
    int n3;
    public Element(int n1, int n2, int n3) {
        this.n1 = n1;
        this.n2 = n2;
        this.n3 = n3;
    }
}


2.これは問題ない

List<Element> list = new ArrayList<>();
/* listに値を入れる処理 */    
Collections.sort(list, Comparator.comparingInt(e -> e.n1));


3.こうするとコンパイルエラー

Collections.sort(list, Comparator.comparingInt(e -> e.n1)
                                .thenComparingInt(e -> e.n2)
                                .thenComparingInt(e -> e.n3));


4.comparingの前に型を指定するか

Collections.sort(list, Comparator.<Element>comparingInt(e -> e.n1)
                                .thenComparingInt(e -> e.n2)
                                .thenComparingInt(e -> e.n3));


5.ラムダ式の引数に型を入れれば通る

Collections.sort(list, Comparator.comparingInt((Element e) -> e.n1)
                                .thenComparingInt(e -> e.n2)
                                .thenComparingInt(e -> e.n3));

3.が通らないのは型を指定しないとComparator<Object>が返るからでしょうか?
ではなぜ、指定していない2.が通り、3.がダメで、4.と5.が通るのか分かりません。
以下に疑問点をまとめます。

2. → 型を指定してないのに通るのはなぜか
3. → 続けてthenComparingを呼べないのはなぜか
5. → ラムダ式に型を入れるだけで4.と同じ効果を得られるのはなぜか

型パラメータについての知識が稚拙ですが、ご教授ください。

追記:
コンパイルエラーは以下が表示されます

n1 は解決できないか、フィールドではありません
n2 は解決できないか、フィールドではありません
n3 は解決できないか、フィールドではありません
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • maisumakun

    2017/11/10 11:01

    「コンパイルエラー」とありますが、どのようなメッセージになるか追記してもらえませんでしょうか。

    キャンセル

  • 退会済みユーザー

    退会済みユーザー

    2017/11/10 11:23

    追記しました。

    キャンセル

  • yohhoy

    2017/11/11 01:21

    FYI: "Comparatorのメソッドを続けて呼び出す"=メソッドチェーンと呼ばれますね。

    キャンセル

回答 2

checkベストアンサー

+2

Javaの総称型に関して、型推論という機能があります。
Collections.sortメソッドの引数は(List<T>, Comparator<? super T>)となっています。このlistの部分にList<Element>が渡っているので、Comparatorの型もElementかその親クラスのものである必要があると推測してくれるのです。

Comparator.comparingIntはComparator<T>を返すstaticメソッドです。このメソッドを渡すと、このTの部分をElementであるとしてコンパイルしてくれるのです。このため、2はコンパイルが通ります。

// Comparator.comparingIntの総称型を自動でElementと推定
Collections.sort(list, Comparator.comparingInt(e -> e.n1));

一方、つないで書いているthenComparingIntメソッドはComparatorのdefaultメソッドで、Comparator<T>のオブジェクトに使うことでComparator<T>を返すインスタンスメソッドのような役割です。
3のComparatorの書き方の場合、最初のcomparingIntメソッドの時点で型を推測する根拠が何もないため、TをObjectとして扱います。これにthenComparingIntメソッドを使うため、TがObjectのままとなります。
つまり、comparingIntやthenComparingIntに渡しているラムダ式のeがObjectとして扱われ、Objectクラスにn1,n2,n3というフィールドがないため、ご質問のエラーが出ます。

// この段階では型推論が効かないため、comparingIntの総称型はObject扱い
// つまり、ラムダ式はToIntFunction<Object>で、eの型がObject扱い
Collections.sort(list, Comparator.comparingInt(e -> e.n1)
// これ以降はComparator<Object>に対してthencomparingIntを呼んでいるため、
// 引数ラムダ式はToIntFunction<Object>
                                .thenComparingInt(e -> e.n2)
                                .thenComparingInt(e -> e.n3));

この問題は、comparingIntにおけるTがElementとわかれば解決します。そのため、総称型を明示的に指定する(4の方法)か、ラムダ式の引数、つまりFunctionの型を明示する(5の方法)ことでコンパイルが通るようになるのです。

// comparingIntの総称型を明示的にElementにすることで、引数ラムダ式をToIntFunction<Element>と推定
Collections.sort(list, Comparator.<Element>comparingInt(e -> e.n1)
// 以降もComparator<Element>に対するメソッドのため、Elementに対する操作とわかる
                                .thenComparingInt(e -> e.n2)
                                .thenComparingInt(e -> e.n3));
// ラムダ式の引数型をElementと明示することて、このラムダ式がToIntFunction<Element>に確定。
// これにより、comparingIntの総称型もElementに確定できる。
Collections.sort(list, Comparator.comparingInt((Element e) -> e.n1)
// 以下、先述通り
                                .thenComparingInt(e -> e.n2)
                                .thenComparingInt(e -> e.n3));

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

2.について
コンパイル時に、Elementとして確定できると型推論してコンパイラが判断してよしなにやってくれるから。

Collections.sort(list, Comparator.comparingInt(e -> e.n1));


においては、コンパイラが第一引数のlistで利用しているElement型の型パラメタで型解決してくれるから。

3.について
下記の場合の戻り値は

 Comparator.comparingInt(e -> e.n1)


時点の戻り値はDefaultメソッドで <T,U extends Comparable<? super U>> Comparator<T> 
として型パラメタの型が確定できません。ですが、2の場合はコンパイラがうまく
やってくれます。ただ、明確には確定できない状態の時には戻り値の型は上記の通り
確定できません。戻り値はT(Object)であり、T型変数はeその時点では確定できないため
T型eにはn2なんてないよ?って怒られます。

ただ、型パラメタの型を確定させてあげれば戻り値の方も引数の型に紐づけられて返すので
4、5はコンパイルが通るということになります。

// この時点で Comparatorクラスのメソッド comparingInt の引数が Element型
// と確定し、同時に戻り値も Comparator<Element> で確定します。
 Comparator.comparingInt((Element e) -> e.n1))
// その戻り値は Element を利用することが確定しているComparatorクラスなので
// 以下呼び出しの引数に定義する関数(ラムダ式)内 e は Element と確定します。
            .thenComparingInt(e -> e.n2)


 // 以下については、当該メソッド内での型パラメタ<T>は Elementとして解釈する
 // というのをジェネリクスで定義しているので、実質上記と等価となり、引数eは
 // Element型、戻り値も Comparator<Element> で確定します。
 Comparator.<Element>comparingInt(e -> e.n1))
            .thenComparingInt(e -> e.n2)
// 上記の呼び出しで、戻り値は Comparator<Element> と確定しているので
// 以下呼び出しの引数に定義する関数(ラムダ式)内 e は Element と確定します。
            .thenComparingInt(e -> e.n3);


上記より、コンパイラが解決できるかどうかだと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.48%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る