Dell new XPS 13 (9300) 2020年モデルを買ったのでUbuntuを入れてみた

Dell new XPS 13 (9300)を買いました

www.dell.com

こちらの32GBメモリモデルを最近買いました。4月下旬に購入して5月下旬に届いて大体2ヶ月ぐらい使っていました。 感想は最高っていう感じです。

 新MBP 13インチやSurface Book 3でも32GBメモリを積めるようになりましたが、購入時点では13インチのノートPCで32GBメモリ搭載可能なのはXPS 13 2 in 1とこのモデルのみだったはずです。

ちょっと大きい14インチで32GBが載るPCはあったんですけどね。

f:id:bitter_fox:20200521001924p:plain

20%オフクーポンとLINEショッピング経由で10%ポイント付与、Visa LINE Payカード決済による3%ポイント還元などで大体20万円弱でこのスペックのマシンが手に入るのはなかなかすごいと思います。

LINEショッピング

ec.line.me

Visa LINE Payカード

www.smbc-card.com

LINEショッピングのポイント還元は数カ月の遅延があるっぽいけど、本当に還元されるかなw ちなみに今の所還元されそうな気配はありません。

Ubuntuをインストールする

ここ数年はずっとMac機を使っていたので、久しぶりのWindows機です。

数年ぶりにWindows機にUbuntuをインストールしました。

軽く手順を残しておきます。

実際には途中紆余曲折しているのでこの手順と全く同じ手順でインストールしたわけではないので、ちゃんといかなかったら悪しからずです。

また、メーカーサポートなども受けられなくなるのでDo it at your own riskでお願いします。

 

基本的には

wiki.archlinux.org

を参考にしました。

Ubuntuインストールメディアを用意する

Windowsのセットアップが完了したらUbuntuをダウンロードしてきてインストールメディアを作成します。

最終的に、今回はUbuntu 18.04を利用しました。

Ubuntu 20.04も試したのですが、XmonadGnomeの相性が良くなくて諦めました。

Ubuntu 20.04ならOut of boxでWiFiに繋げられたので、Xmonadを使わないならUbuntu 20.04を使ったほうが良いです。

Ubuntu 18.04を利用する場合は有線でインターネットに接続する手段を用意しておいてください。

僕はスマホのUSBテザリングを用いました。

 

日本語Remixが好きなのでここからisoイメージをダウンロードします。

www.ubuntulinux.jp

インストールメディアにはUSBメモリを用いました。

 

インストールメディアの書き込みはLinuxLive USB Creatorを用いました。

www.linuxliveusb.com

パーティションを切る

Windows側で予めパーティションを切ります。

100GBをWindows側に残して、残りの840GB程度をLinuxに使えるようにパーティションを切りました。

BitLockerを解除する

BitLockerというディスク暗号化が行われています。

これが有効のままで後に行うUEFI Secure Bootを解除すると、Windowsを起動するたびにディスク復号のために復号キーの入力を求められます。

超面倒くさいので暗号化自体を解除します。

これを無効にするとPCが盗難されたときにSSDからデータを引き抜くことが容易にできるようになるので、Do it at your own riskでお願いします。

コマンドラインを管理者モードで起動して次のコマンドを入力します。

 

manage-bde -off c:

 

manage-bde -statusで暗号化が解除されるのを待ちます。

RAIDIntel RSTを解除するAHCI

SSDRAIDで構築されてます。

このままだとUbuntuのインストールができないのでRAIDを解除してAHCIに変更します。

このプロセスは手順を間違えるとWindowsの起動ができなくなるので手順に注意してください。

起動できなくなったらRAID構成に戻すと起動できるようになるかも・・・

triplescomputers.com

こちらで紹介されている手順を行ってBIOS設定を変更します。

ポイントはBIOSで設定を変更する前にWindowsで行う処理があります。

  • Windows Start Menuを右クリックして「コマンドプロンプト(管理者)」を起動します
    • コマンドプロンプトが一覧にない場合、すでに最新のWindowsにバージョンアップされている可能性があります。その場合は、次の手順でコマンドプロンプトを起動します。
      • Startボタンをクリックして、cmdと入力します
      • 結果を右クリックして「管理者として実行」を選択します
  • 「bcdedit /set {current} safeboot minimal」と入力してエンターを押下します
  • 上記のコマンドが動かない場合は、「bcdedit /set safeboot minimal」を試してみて下さい
  • コンピュータを再起動してBIOS設定に入ります。(BIOS設定に入るために押下するキーは機種によって異なります)
  • SATA OperationモードをIDERAIDからAHCIに変更します(重ね重ね、表記は機種により異なります)
  • 変更を保存して、BIOS設定を終了すると、Windowsがセーフモードで自動的に起動します
  • Windowsスタートメニューを右クリックしてコマンドプロンプト(管理者)を選択します
  • 「bcdedit /deletevalue {current} safeboot」を入力してエンターキーを押します
    • 上記で別のコマンドを入力した場合は、「bcdedit /deletevalue safeboot」を試してみてください
  • もう一度再起動するとAHCIドライバが有効になった状態でWindowsが自動的に起動するはずです

Secure Bootを無効にして、Boot SequenceをUbuntuインストールメディアが優先されるように変更する

ここまで来たらあとはいつもの手順です。

Ubuntuを起動して、Ubuntuをインストールする

この辺はいつもと同じ手順なので省略します。 初めてインストールする方は他のブログやウェブページなどを参考にしてください。

僕は「それ以外」を選択して、さっき作ったパーティションをマウントポイント/で初期化してそこにgrubブートローダを入れてます。

なんとかしてインターネットに接続して、パッケージをアップデートする

パッケージ(主にkernelバージョン)をアップデートするとWiFiが使えるようになります。

この時点ではWiFiが使えないので、USBテザリングや有線LAN接続をするなどしてインターネットに接続して、以下のコマンドをターミナルから実行します。

sudo apt update
sudo apt upgrade

(必要に応じて)Linux Kernelをダウングレードする

GPUがハングしてクラッシュするバグが報告されています。

bugs.launchpad.net

 

実際に5.3.0-53でも同様の事象が起きました。 5.3.0-45ならこの問題は起きないようなので、Linux Kernelをダウングレードします。

より新しいバージョンを利用していて既に修正されている場合はこれは必要ありません。

 

sudo apt install linux-image-5.3.0-45-generic linux-headers-5.3.0-45 linux-modules-extra-5.3.0-45-generic

grub-customizerでgrubメニューを変更して、再起動するといい感じに5.3.0-45が起動するはずです。 f:id:bitter_fox:20200809182736p:plain

(必要に応じて)Mesaをアップグレードする

IntelliJ IDEAやgnome terminalを使用しているとGPUがハングしてクラッシュする問題が報告されています(主にスクロール時にハングが発生します)。

gitlab.freedesktop.org

グラフィックスドライバの問題のようで、バージョンアップすると治るようです。

sudo add-apt-repository ppa:kisak/kisak-mesa
sudo apt-get update

sudo apt upgrade

(必要に応じて)IntelliJ IDEAの設定を変更する

IntelliJ IDEA 2020を使っていると、Mesaをアップグレードしてもクラッシュする問題が残ります。

support.samuraism.com

https://youtrack.jetbrains.com/issue/JBR-2310

cat /proc/cpuinfo | grep microcodeの結果が150でないとこの問題があります。

% cat /proc/cpuinfo | grep microcode
microcode   : 0x78
microcode   : 0x78
microcode   : 0x78
microcode   : 0x78
microcode   : 0x78
microcode   : 0x78
microcode   : 0x78
microcode   : 0x78

この場合は120なのでこの問題が発生します。

Macだとアップデートが提供されだしているようですが、Linuxだとまだっぽいので、Workaroundでしのぎます。 サムライズムさんのブログの回避策1と2両方適用していて、今の所クラッシュは起きていません。

-Xms4096m
-Xmx4096m
-XX:ReservedCodeCacheSize=240m
-XX:+UseConcMarkSweepGC
-XX:SoftRefLRUPolicyMSPerMB=50
-ea
-XX:CICompilerCount=2
-Dsun.io.useCanonPrefixCache=false
-Djdk.http.auth.tunneling.disabledSchemes=""
-XX:+HeapDumpOnOutOfMemoryError
-XX:-OmitStackTraceInFastThrow
-Djdk.attach.allowAttachSelf=true
-Dkotlinx.coroutines.debug=off
-Djdk.module.illegalAccess.silent=true
-Dawt.useSystemAAFontSettings=lcd
-Dsun.tools.attach.tmp.only=true
-XX:-TieredCompilation
-XX:TieredStopAtLevel=1
-XX:+UnlockExperimentalVMOptions
-XX:hashCode=0

今の所こんな感じ。

(必要に応じて) throttledをインストールする

CPUのサーマルスロットリングがかなりコンサバティブに設定されています。 CPUパワーが必要な処理をしている場合でも3GHzを超えたと思ったら2GHzを切るというような挙動をして十分なパワーを持続的に得られないような設定になっています。 もっとパワーを持続的に出したい場合はthrottledをインストールすると良いようです。

github.com

sudo apt install git build-essential python3-dev libdbus-glib-1-dev libgirepository1.0-dev libcairo2-dev python3-venv python3-wheel
git clone git@github.com:erpalma/throttled.git
sudo ./throttled/install.sh

sudo systemctl stop thermald.service
sudo systemctl disable thermald.service

sudo systemctl mask thermald.service

あとはお好みの設定をする

僕はこんな感じでアプリケーションとか設定を管理してます。

github.com

基本的にはこのスクリプトを適宜実行していけば設定とかが完了します。 リンクとかが古くなっているものがあるのでそれも随時直しながら・・・

github.com Xmonadの設定はこんな感じです。

残りの問題

Apple MagicTrackpad2の接続断がたまに発生する。

blog.hanhans.net

この方が追記で言及されている問題と同じ問題です。

Linux Kernelにパッチを当ててみましたが解決した印象は無いです。

git clone git://kernel.ubuntu.com/ubuntu/ubuntu-bionic.git
cd ubuntu-bionic
git checkout tags/Ubuntu-hwe-5.3.0-45.37_18.04.1
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index dea9cc65bf80..94fbdc4411f3 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -310,6 +310,15 @@ static const struct hid_device_id hid_battery_quirks[] = {
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
                USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI),
          HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE },
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
+         USB_DEVICE_ID_APPLE_MAGICMOUSE),
+         HID_BATTERY_QUIRK_IGNORE },
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
+         USB_DEVICE_ID_APPLE_MAGICTRACKPAD),
+         HID_BATTERY_QUIRK_IGNORE },
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
+         USB_DEVICE_ID_APPLE_MAGICTRACKPAD2),
+         HID_BATTERY_QUIRK_IGNORE },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM,
                USB_DEVICE_ID_ELECOM_BM084),
          HID_BATTERY_QUIRK_IGNORE },
static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type, struct hid_field *field)
{
       return 0;
}

hidinput_setup_batteryでどのデバイスに対しても何もしないというのも試していますが、問題は起き続けています。

株式会社はてなに入社しました

この度、緑あって、株式会社はてなに新入社員として入社する運びと相成りました。

大学、大学院では大変お世話になりました。
この場を借りて御礼申しあげます。
今後とも、引き続きご指導ご鞭撻賜りたく存じます。

株式会社はてなに入社しました - hitode909の日記

これは近況のはなsh,いや,明日の出来事か.

この記事はRCC OBOG Advent Calendar 2017の11日目の記事です.
昨日はik11235さんのはずでした.
明日はHikoPatchiさんのはずです.

2012年度入学のbitter_foxです.2016年に大学を卒業したので,いわゆるのっきー世代というやつです.といっても,のっきー世代は3世代ぐらいに渡っている気がする.
2014年度の執行委員長をしていました.ザ・老害です.

本題に入る前にちょっと余談.
この企画の意図についてです.
最近弊ラボの冷蔵庫が壊れた時に,研究室のOBOGから寄付を募ってなんとか乗り切ったのですが,その時にOBOGと現役生の繋がりが大事だなって思ったのですね.
そこで,OBOG会よりももう少し軽い感じでRCCの現役生がOBOGの雰囲気を知れる機会があれば良いなという気持ちでアドカレを作ってみた次第です.
もちろん,僕らが,OBOGが最近何やっているのかを知れたら楽しいかなという気持ちもあります.
参加者各位におかれましては良い感じにリレーをしていってくれて大変感謝しています.

老害各位の近況を教えてください」と書いたは良いものの,僕は近況の話ではなく明日(12日,火曜日)の話をしようと思います.

明日,大学院進学ガイダンスでお話をします.
話す内容としては,大学院に何で行ったのとか,大学院と学部の違いとか,LINEに就職する話とかをします.
大学院への進学を考えているRCCの皆さんも,迷っている皆さんも,全く考えていない皆さんも是非賑やかしに遊びに来てほしいです!
B3以外もOKらしいですよ.

で,これだけだとアドカレとしてはちょっと寂しいので,プレゼンを作るときに使って便利だったツールを紹介します.

発表資料で,いい感じのタイムライン(何月に何をして,何月から何月まで何何をしていた,みたいなの)を作りたくて,色々ツールを探していたのですが,
https://time.graphics/
が割と良い感じでした.

良いポイントとしては
・横軸でプレゼンに使いやすい
・イベントの入力がしやすい
・インテグレーションが豊富(Google Calendarとか)

例を見ると雰囲気解ると思います.

ちなみに僕が作ったのはこんな感じ
https://time.graphics/line/29270

iframeに対応していないので画像で・・・

ね?良さそうじゃないです?

GSoC 2017でChecker Frameworkに貢献してきた

Google Summer of Code(GSoC) 2017に参加し,完遂しました.
雑な感想です.

GSoCはGoogleが実施しているイベント(?)で,学生が夏休みの間OSSに貢献するためにいい感じの機会を提供している感じです.
2〜3月にGSoCと提携しているOSSプロジェクトに対して何をしたいかの提案書を書き,採用されれば6〜8月の間コードを書きOSSに貢献します.
6〜8月の間は3回に分けGoogleから報奨金が支払われます.
参加者の国によって額は異なりますが,日本だと大体60万円相当です(ドル建てなので支払い時のドル円によって異なる).

貢献したプロジェクトはChecker Frameworkです.
Checker FrameworkはJavaプログラムにある型システムに基づく型を型アノテーションを用いて記述し,プログラム内に型エラーが無いかを検査するためのフレームワークです.
Java8で導入された型アノテーションのきっかけになったプロジェクトです.
櫻庭さんの記事が分かりやすいと思うので詳しくはそちらを読むと良いと思います.
このプロジェクト内のデータフロー解析周りを弄くっていました.

良かったこと

  • 気になっていたプロジェクトに参画できた
  • ビデオチャット(英語)を頻繁にやったので英語力が上がった(気がする)
  • データフロー解析周りの知識がついた
  • Project Type AnnotationsのSpec LeadやEGの人らと関われた
  • 60万円手に入った

辛かったこと

  • 大学が7月末まであるので忙しくて辛い
  • ミーティングがビデオチャット(英語)なので辛かった
  • メンターが忙しすぎてレビューが結構滞った

まとめ

基本的には良いことだらけなので皆余裕があれば参加すると良いと思います.
プロジェクトもピンキリであるので,適切な難易度を選べば技術力でめちゃくちゃ苦労するということは無いと思います(当然それなりの苦労はありますが・・・).
英語も最初のうちは戸惑うけど,何度も聞き直せば何度も伝わるように言ってくれるのでなんとかなりました.

提案書の採用では既にOSS活動を行っていたりすると有利に働くので,GSoCの募集が始まるよりも前の今のうちから色々すると良いと思います.

nesting printhing

このエントリは Java Puzzlers Advent Calendar 2016 の 14 日目です。

class Main {
    abstract class Person {
        protected String name;
	
        public Person(String name) {
            this.name = name;
        }
	
        public abstract void print();
    }
	
    protected void print(String s) {
        System.out.println("[" + s + "]");
    }
    
    public void run() {
        Person p = new Main() {
            protected void print(String s) {
                System.out.println("\"" + s + "\"");
            }
        }.new Person("foobar") {
            public void print() {
                System.out.println("This is Person class: ");
                print(name);
            }
        };
        p.print();
    }

    public static void main(String[] args) {
        new Main().run();
    }
}
1.
This is Person class:
[foobar]

2.
This is Person class:
"foobar"

3. コンパイル時エラー

4. 実行時例外

解説は後ほど


Twitterでアンケートを取ると,以下のような結果になりました.

結果を見ると2. "foobar"が48%と一番多く,僅差で3. コンパイル時エラーが35%と多いですね.
1. [foobar]は15%と少なく,4. 実行時例外はごくわずかでした.

それでは答えです.
javacでコンパイルして,javaで動かすと,下の画像のようになります.

なんと,予想の少なかった1. [foobar]が正解ですね.











そろそろ「ちょっと待った!手元で動かしたら,コンパイル時エラーになるんだけど!!!」という声が聞こえてきそうです.
おや,おかしいですね.
先ほど動かしたJavaのバージョンを確認してみましょう.

JDK7での結果は1. [foobar]のようでした.

それではJDK8ではどのような結果になるのでしょうか.
実行結果がこちらです.

仰るとおり,3. コンパイル時エラーになりました.
どうやらJDKのバージョンによって挙動が違うようです.
JDKのバージョンで挙動が違うこのコードですが,Java言語の仕様としてはどちらが正しいのでしょうか(言語仕様が変わったという可能性もありますが,今回のコードについては違います).

JDK8では適切なメソッドが見つからなかったというエラーなので,メソッドの選択についての言語仕様を見てみましょう.
15.12. Method Invocation Expressions15.12.1. Compile-Time Step 1: Determine Class or Interface to Searchです.

If the form is MethodName, that is, just an Identifier, then:

If the Identifier appears in the scope of a visible method declaration with that name (§6.3, §6.4.1), then:

If there is an enclosing type declaration of which that method is a member, let T be the innermost such type declaration. The class or interface to search is T.

This search policy is called the "comb rule". It effectively looks for methods in a nested class's superclass hierarchy before looking for methods in an enclosing class and its superclass hierarchy. See §6.5.7.1 for an example.

賢くなったGoogle翻訳で翻訳すると

フォームがMethodNameの場合、つまり識別子だけの場合は、次のようになります。

識別子が、その名前(§6.3、§6.4.1)を持つ可視メソッド宣言のスコープに現れた場合は、次のようになります。

そのメソッドがメンバーである囲み型宣言がある場合、Tをそのような型宣言の最も内側の宣言とします。 検索するクラスまたはインタフェースはTです。

この検索ポリシーは「くしルール」と呼ばれます。 これは、囲みクラスとそのスーパークラス階層のメソッドを探す前に、ネストされたクラスのスーパークラス階層のメソッドを効果的に探します。 例については、§6.5.7.1を参照してください。

つまり,「クラスがネストされていた場合,そのクラスか親クラスからメソッド名で検索するよ.見つかったら,外の環境にあるメソッドは検索しないよ」という意味です.
今回のコードの場合,printという名前のメソッドがすでにPersonにあるので,Mainのprintを見ないというのが正しい挙動のようです.
ですので,JDK7の挙動はバグであり,JDK8で修正された結果,挙動が変わっています.

バグについては,こちらの記事をご参照ください.

ということで,3. コンパイル時エラーが正解でした.


ここから先は余談です.

Mainのprintを呼び出せるようにするには,Main.printかPerson.printの名前を変更すればよいです(Main.this.printとして呼び出しても良い).
Main.printの名前を変えて動かしてみましょう.もちろんJDK8で動かします.

ちゃんと動くように修正したため,挙動は1. [foobar]か2. "foobar"のいずれかです(例外は起きそうにないですね).
動かしてみると,投票結果では一番多かった2. "foobar"を差し置いて,1. [foobar]が出力されました.
printNameメソッドは,直前のnew Main() {...}ではなく,runを実行しているMainクラスをレシーバとして実行するため,このような挙動になりました.
以下のようにMain.thisを補うと,どれを参照しているのかが分かりやすくなると思います.

        new Person("foobar") {
            @Override
            public void print() {
                System.out.println("This is Person class: ");
                Main main = Main.this;
                main.printName(name);
            }
        };

以下のように,runメソッドを実行するMainクラスのprintNameの実装を変えると,2. "foobar"が出力されます.

class Main {
    abstract class Person {
        protected String name;
	
        public Person(String name) {
            this.name = name;
        }
	
        public abstract void print();
    }
	
    protected void printName(String s) {
        System.out.println("[" + s + "]");
    }
    
    public void run() {
        Person p = new Person("foobar") {
            @Override
            public void print() {
                System.out.println("This is Person class: ");
                printName(name);
            }
        };
        p.print();
    }

    public static void main(String[] args) {
        new Main() {
            @Override
            protected void printName(String s) {
                System.out.println("\"" + s + "\"");
            }
        }.run();
    }
}

この問題をまとめると,「こんな分かりにくいコードを書くな」に尽きるでしょうか.

Tricky min

この記事は Java Puzzlers Advent Calendar 2016 の 10 日目です。

import java.util.stream.*;
import java.util.function.*;

class Main {
    public static void main (String[] args) throws java.lang.Exception {
        System.out.println(
                Stream.of(2, 10, null, 100, 43)
                        .<UnaryOperator<Integer>>map(n -> m -> Integer.min(n, m))
                        .reduce(UnaryOperator.identity(), UnaryOperator::andThen)
                        .apply(Integer.MAX_VALUE));
	}
}
1. 2
3. 0
4. 2147483647
5. コンパイル時エラー
6. 実行時例外

解説は後ほど


nullの要素でぬるぽが出ると予想した人が多かったのではないでしょうか?
あるいは,Integer.minはnullを無視してくれて,2が出力されるのではと予想したもおられるかもしれません.

正解は5. コンパイル時エラーです.

出力されるコンパイルエラーは以下のようなものです.

|  Error:
|  不適合な型: ラムダ式の戻り型が不正です
|      型変数Vのインスタンスが存在しないので、java.util.function.Function<java.lang.Integer,V>はjava.util.function.UnaryOperator<java.lang.Integer>に適合しません
|                          .reduce(UnaryOperator.identity(), UnaryOperator::andThen)
|                                                            ^--------------------^

よく分からないですよね.UnaryOperatorのJavadocを見てみましょう.
UnaryOperator (Java Platform SE 8)

andThenメソッドはFunctionから継承しています.andThenメソッドのシグネチャは以下のようになってます.

default <V> Function<T,V> andThen(Function<? super R,? extends V> after)

簡単に言うとUnaryOperator::andThenは

<V>(UnaryOperator<Integer>, Function<? super Integer, ? extends V>) -> Function<Integer, V>

という型のメソッド参照になります.
一方で,reduceは(UnaryOperator, UnaryOperator) -> UnaryOperatorを求めるので「型が違うじゃねーか.そもそもV無いよ」と怒られているわけです.

修正は簡単です.andThenを使わずに自力でandThen相当の処理を書けばよいです.

import java.util.stream.*;
import java.util.function.*;

class Main {
    public static void main (String[] args) throws java.lang.Exception {
        System.out.println(
                Stream.of(2, 10, null, 100, 43)
                        .<UnaryOperator<Integer>>map(n -> m -> Integer.min(n, m))
                        .reduce(UnaryOperator.identity(),
//                                (before, after) -> n -> before.apply(after.apply(n)))
                                (before, after) -> before.andThen(after)::apply)
                        .apply(Integer.MAX_VALUE));
	}
}

ちなみにこれで実行すると,ぬるぽが出ます.
理由はInteger.minはintの値2つを取ります.
つまり,オート(アン)ボクシング発生してしまうためです.

ぬるぽが出ないようにするために,filterでnullを除外しましょう.

import java.util.stream.*;
import java.util.function.*;

class Main {
    public static void main (String[] args) throws java.lang.Exception {
        System.out.println(
                Stream.of(2, 10, null, 100, 43)
                        .filter(Objects::nonNull)
                        .<UnaryOperator<Integer>>map(n -> m -> Integer.min(n, m))
                        .reduce(UnaryOperator.identity(),
//                                (before, after) -> n -> before.apply(after.apply(n)))
                                (before, after) -> before.andThen(after)::apply)
                        .apply(Integer.MAX_VALUE));
	}
}

これで2が出力されます.

ところで,minを求めるのにこんな面倒臭いことはしないですよね.Stream.minを使うともっとシンプルに書けます.

import java.util.stream.*;
import java.util.function.*;

class Main {
    public static void main (String[] args) throws java.lang.Exception {
        System.out.println(
                Stream.of(2, 10, null, 100, 43)
                        .filter(Objects::nonNull)
                        .min(Comparator.naturalOrder())
                        .orElse(Integer.MAX_VALUE));
	}
}

module-info.javaのディレクティブまとめ

JavaOne 2016に来ています.
去年はJigsawのセッション取っても,はてなが並ぶばっかりでしたが,意外とJigsawの話が分かるようになってきました.

module-info.javaのディレクティブ(requiresとか)の簡単なまとめです.

exports

module m {
    exports p;
}

packageを他のモジュールからアクセス可能にします.
エクスポートされたパッケージ内のpublicクラスは含むモジュールをリード(後述)できれば,アクセス可能です.

exports to ...

module m {
    exports p to m1, m2;
}

packageを特定のmoduleからのみアクセス可能にします.
...に指定されていないモジュールからはアクセスができません.

exports private

以下のように通常のexportsでは,他のモジュールがprivateなフィールドやメソッドに対してリフレクションでアクセス権を付与しようとすると,実行時例外が発生するようになります.

package m1.p;
class C {
    private String field = "...";
}
module m1 {
    exports m1.p;
}
package m2;
class UsePrivate {
    void method() {
        C c = ...;
        Field field = c.getClass().getDeclaredField("field");
        field.setAccessible(true); // throws Exception
    }
}

exports privateを用いるとこれが可能になります.

module m1 {
    exports m1.p;
}
package m2;
class UsePrivate {
    void method() {
        C c = ...;
        Field field = c.getClass().getDeclaredField("field");
        field.setAccessible(true); // throws Exception
    }
}

exports private to ...

モジュール制限付きexportsかつprivateな要素へのアクセス権限付与を可能にします.

requires

moduleを読み込みます.
requiresするモジュール内でmodule内でエクスポートされたクラスにアクセス可能になります.

requires transitive

推移的なmoduleの読み込みです.
requires transitiveが記述されたモジュールを読み込んだモジュールはrequires transitiveで指定されたmoduleを読み込みます.
モジュールmがあり,モジュールm1がmをrequires transitiveで読み込んでいたとします.

module m {
    exports my.library;
}
module m1 {
    requires transitive m;
}

この時m2がm1を読み込むと,推移的にmも読み込み,my.libraryパッケージのアクセスが可能になります.

module m2 {
    requires m1; // 推移的にmも読み込む
}

requires static

moduleを読み込みますが,コンパイル時のみにmoduleの存在が必須です.
通常のrequiresでは,moduleが存在していなければコンパイル時やランタイム時にエラーとなります.
ランタイム時にmoduleが存在しなくても良い場合に,これを使います.

具体的なユースケースはよく分からないですけど,コンパイル時にのみ有効なアノテーションを使うときとかかなぁ・・・?

参考

Java Platform Module System: Issue Summary
Proposal: #CompileTimeDependences: `requires static`


ここに上げた以外にも,provides withやusesがあります.
それらについてはJigsawでSPIを使用する - きつねとJava!を参考ください.