ITお絵かき修行

3歩歩いても忘れないために

AWS Summit Tokyo 2016メモ【The State of The Art - AWS / EC2 Networking (同時通訳)】

AWS Summit Tokyo 2016に参加した。
下記セッションに参加したので、参加メモを書いておく。
※正確な情報・見解は後日公開される(であろう)登壇者のスライドや公式資料等を参照のこと。
www.awssummit.tokyo

The State of The Art - AWS / EC2 Networking (同時通訳)]

Kevin Miller (Director, Software Development, EC2 Networking, Amazon Web Services, Inc.)

【概要】

EC2における、LinuxカーネルレベルでのTCPネットワークパラメータチューニングの話。
AWS,EC2に拘った内容というよりは、Linux全般でのTCPチューニングの話だった。
とにかく話が早くあっという間に終わった。

【内容】

TCPの性能

TCPSSH、HTTP等のベースプロトコルであり、送信側と受信側それぞれが互いに接続しあう。
TCPの特徴として、異なる大きさの送信/受信ウィンドウを持つ。
輻輳制御ウィンドウ:送信側が制御する。輻輳制御アルゴリズムで制御する。
・パケットの損失(パケットロス)がスループットに与える影響について触れていた。
 TCPの統計値取得   :netstat -s | grep retransit
 ソケットレベルの診断:ss -ite
・パケットの再送状況をリアルタイムに取得できるコマンドがgithub上にあるとのこと。詳しくは下記を参照。
github.com

Linux輻輳制御アルゴリズム
1.BIC (Linux2.6.6~2.6.18)
2.CUBIC(Linux2.6.19以降)
TCP Renoに代わるアルゴリズム

・再送タイマー
パケットロスした、とみなす時間の設定。
短すぎると:過剰反応してしまい、パフォーマンスが落ちる。
長すぎると:パケットロスの検知が遅れ、パフォーマンスが落ちる。
デフォルトは200ミリ秒。

・ip route list
AWSの場合、サブネット内の他のインスタンスへルーティングしている場合があるので、そこの再送時間の設定を変える。(例えば10ミリ秒にする等)

・ネットワークパスのチューニング
パス上のルータにネットワークバッファがある。
→NWへの負荷が高まると、再送が多くなり、結果としてタイムアウトを引き起こす原因となる。

・ネットワークバッファのアクティブキュー管理
→tc qdisc list
・fq_codel
→チューニングが不要な、qdiscのアルゴリズム

・EC2拡張NW機能
EC2で提供するAmazonLinux,Windows(WindowsServer?)に組み込まれたドライバにて利用することができる。詳しくは下記を参照。
www.youtube.com

・MTU

Maximum Transmission Unit (MTU)は、ネットワークにおいて1回の転送(1フレーム)で送信できるデータの最大値を示す伝送単位のこと。
Maximum Transmission Unit - Wikipedia

多くのドライバは1500バイトを設定することができる。しかし上記の「EC2拡張NW機能」のドライバを使うと9000バイトまで設定することができる。よってオーバヘッドを減らすことができる。
→MTUの設定値は「ip route list」から確認することができる。

②応用

実際に上記で取り上げた要素を使って、測定を行った結果を紹介していた。

まとめ

・ネットワークはブラックボックスではない。Linuxの各種ツールを使用することで理解することができる。
・設定の調整により、性能を飛躍的に向上させることができる。
・ネットワークからアプリケーションのニーズを捉え、調整することが重要。

JavaDayTokyo2016メモ【3-E: Javaの進化にともなう運用性の向上はシステム設計にどういう変化をもたらすのか】

JavaDayTokyo2016に参加した。
下記セッションに参加したので、参加メモを書いておく。
www.oracle.co.jp

[3-E: Javaの進化にともなう運用性の向上はシステム設計にどういう変化をもたらすのか]

TIS株式会社
アプリケーション開発センター シニアエキスパート
川島 義隆 氏

【概要】

Java9で追加された新機能を使った設計・実装の紹介
Javaの進化にともなう運用性の向上はシステム設計にどういう変化をもたらすのか

【内容】

1.nullチェック

・Optionalクラスを使いましょう、という話。
・Optionalクラスの使い方としては、nullが発生しうる実装個所を包み込むように実装すべき。
Javaの個々のAPIはnullチェックを意識した戻り値となっていないので、
Optionalを使ってAPI単位でのnullチェックを行うべき。

2.Mixin

mixin とはオブジェクト指向プログラミング言語において、サブクラスによって継承されることにより機能を提供し、単体で動作することを意図しないクラスである。
Mixin - Wikipedia

Javaは多重継承が認められていない。そのため、ミドルウェアパターンの実装を行う場合は、ミドルウェアに対応するサブクラスを実装する。(例:ResultSet)
Javaミドルウェアパターンを実装するときは、事前に(※I/F側に)多くのメソッドを予め用意しておく場合が多い。
・Java8以降ではI/Fにデフォルトメソッドを持つことが可能となったので、共通的な実装をI/F側で持つことができるようになった。
→ただし状態を持つことはできないので、デフォルトメソッド内からI/Fの別メソッドを参照するような実装をしておき、実装クラス側からオブジェクトを渡すよう強制する、といった方法をとることができる。
・動的mixinの方法として、MethodHandleというクラスがJava7から追加された。
→ただし従来のリフレクションの10~100倍遅い。

3.システム挙動の動的な変更(using REPL)

・アプリケーションに管理用APIを用意するには、という話。
・enkanというFWでは疑似REPLが用意されており、REPLからMWの状態を変更することが可能。
→enkanではJShellを取り込む、とのこと。

4.Java9時代の無停止デプロイ

●従来
①LBでサーバごと切り替え
→問題点:サーバが2台ないし3台必要。
②APサーバ(コンテナ)で対応
→問題点:メモリリーク

●これから
JVMのプロセス単位で切り替え


・SO_REUSEPORT((Linux kernel 3.9~より利用可能。同じuidから利用可。Windowsでは不可)という仕組みを利用して、同一ポートに対してリッスンするプロセスを複数立ち上げる。
・SO_REUSEPORTの利用には、ServerSocketChannelクラスのメソッドを実装したAPサーバが必要。現状だとJettyくらいしか選択肢がない。
・falchion-containerというJVMプロセスをプールするコンテナを作られたそう。
JMX・JStatで監視。

5.まとめ

・過剰設計が必要だったJavaも、必要なものだけを作ればよいという時代になった。
シンタックスのキャッチアップだけではなく、設計手法のアップデートもしよう。

JavaDayTokyo2016メモ【2-A: Project Jigsawではじめるモジュール開発】

JavaDayTokyo2016に参加した。
下記セッションに参加したので、参加メモを書いておく。
※正確な情報・見解は後日公開される(であろう)登壇者のスライドや公式資料等を参照のこと。
www.oracle.co.jp

[2-A: Project Jigsawではじめるモジュール開発]

Java in the Box 櫻庭 祐一 氏

【概要】

Project Jigsawの導入背景、モジュールの作成方法・使用方法の紹介

【内容】

1.導入背景

(1)現状
・現在のJavaには「クラスパスが複雑」「rt.jarの肥大化」という2つの課題を抱えている。
・上記に関連し、「JAR HELL」と呼ばれるような.jarファイル間の依存関係の複雑さも発生している。

(2)(1)の原因
・クラスパス・JARのパッケージング機構における、
「依存関係」「バージョン」「公開範囲」をコントロールする仕組みが不足しているため。
・特にクラスのpublic属性のアクセス範囲が広すぎることが大きな原因とみられている。
 →『public is TOO public』

(3)歴史
・2005年頃から提言はされていた。
・JSR277、JSR294といったJSRを経てProject Jigsawが発足し、Java9で採用。

2.モジュールの作成

・モジュールを作成する際の定義内容は以下の3要素。

Module extends JAR{
    dependency …「依存関係」
    export   …「公開範囲」
    version   …「バージョン」 ※バージョンに対応する仕組みはまだない。今後どうなるか不明とのこと。
}

・モジュールはJARの拡張。JARと同じくクラスパスに追加しクラスを読み込ませることは可能。

・今までのpublicクラスは誰からでもアクセス可能だった。これからの(※モジュール内の)publicクラスは、公開範囲に対応するクラスからしかアクセスできなくなる。
 →今後はmainメソッドを含むクラスを明示的に公開しないと、アプリが起動できなくなる。

・モジュールの定義内容は「modules-info.java」に記述する。「modules-info.java」は「src」フォルダ直下に置く。(※読み込み効率、複数ファイル対応etcの為)
例:modules-info.java

module fxdemo {
    requires javafx.controls;
    requires javafx.graphics;
}
3.モジュールの使用

(1)コンパイル
・jarコマンドに「--create」などのオプションを付与して実行する。tarっぽいらしい。

(2)実行
・実行時はオプションを指定して実行する。
 「モジュールのみ使用」「モジュールとクラスパス」といった使い方が可能。
例:実行コマンドの先頭部

java -mp mods -addmods ・・・

・mainメソッドの実行コマンドは「モジュール名/クラス名」となる。

4.MISC about Module

・「jdeps」…自分たちでモジュール(modules-info.java)を作成する際に使用できるツール。クラス、jarファイルの依存関係を出力できる。
・「jlink」…JREのカスタマイズが可能となるツール。組み込み用のライブラリのみ抜き出す、コアライブラリのみ抜き出すといったことが可能となる。


[感想など]
・モジュールを使った構成管理のプロセスが、antやGradleといったビルドツールで実現可能なのかが気になった。
・発表資料は毎回JavaFXで作成されているとのこと。

Upgrade Java SE 7 to Java SE 8 OCP Programmer(1Z0-810)対策 第三章 ラムダ式を使用するコレクション(1)

第三章よりラムダ式を使用したstreamに対するフィルタリングを取り扱う。

■注意
正確な情報は下記リンクを参照してください。

■試験対策ページ
java.boot.by

■試験
Upgrade Java SE 7 to Java SE 8 OCP Programmer | Oracle Certification Exam

3.1. forEach()メソッドメソッド・チェーンを使用したコレクションの反復処理

繰り返し処理を制御するイテレータには一般的に2つの実装アプローチが存在する。

Activeな(外部)イテレータ
Activeなイテレータでは、イテレータの制御者が、次の要素へ処理が移ったことを伝えたり、各要素へアクセスした際に処理を行ったり等、制御者自身が生成した繰り返し処理を細かく制御できる。
Java1.0、1.1ではVectorとHashtableというコレクションクラスと、Iteratorデザインパターンを実装したEnumrationと呼ばれるクラスがあった。

Vector names = new Vector();
names.add("George");
names.add("Fred");
names.add("Ron");

Enumeration e = names.elements();
while (e.hasMoreElements()) {
    String name = (String) e.nextElement();
    System.out.println(name);
}

Java1.2ではIteratorデザインパターンIteratorというクラスに実装された。Iteratorでは未だに返却値のキャストが必要とされていた。Java1.2から1.4にかけて文字列の繰り返し処理はは下記のようなコードだった。

List names = new ArrayList();
names.add("George");
names.add("Fred");
names.add("Ron");

Iterator i = names.iterator();
while (i.hasNext()) {
    String name = (String) i.next();
    System.out.println(name);
}

Java5ではジェネリクスが導入され、Iteratorインタフェースクラスとforループの実装が改修された。

// Java SE 5 !!!
package java.lang;

import java.util.Iterator;

public interface Iterable<T> {
    public Iterator<T> iterator();
}
package java.util;

public interface Iterator<E> {
    public boolean hasNext();
    public E next();
    public void remove();
}

メモ:forループの内部では Iterator.hasNext() と Iterator.next()が呼ばれている。そのためActiveなイテレータとみなす事ができる。

List<String> names = new ArrayList<String>();
names.add("George");
names.add("Fred");
names.add("Ron");

for (String name : names) {
    System.out.println(name);
}


Passiveな(内部)イテレータ
Passiveなイテレータではイテレータ自身が繰り返し処理を制御する。イテレータの制御者は基本的に、コレクション内の要素に対するいくつかの操作をイテレータに行うよう指示します。Java8ではこのアプローチが利用可能である。

Java8ではjava.lang.IterableインタフェースクラスがforEachメソッドを持つ

package java.lang;

import java.util.Iterator;
import java.util.function.Consumer;

public interface Iterable<T extends Object> {

    public Iterator<T> iterator();

    public default void forEach(Consumer<? super T> cnsmr) {
        // ...
    }

    ...
}

java.util.Collectionインタフェースクラスはjava.lang.Iterableクラスを継承している。そのためjava.util.List もしくは java.util.Setインタフェースクラスの実装クラスは自動的にforEachメソッドを持っている。
java.util.Mapインタフェースクラスはjava.util.Collectionインタフェースクラスを継承していない。しかしJava8からは一貫性のためにforEachメソッドを持つ。
このメソッドはひとつのパラメータをとる関数型インタフェースである。そのため実体のパラメータを通すforEachメソッドラムダ式の候補となる。

List<String> names = new ArrayList<>();
names.add("George");
names.add("Fred");
names.add("Ron");

names.forEach(name -> System.out.println(name));

もしくはメソッド参照を使用する。

...
names.forEach(System.out::println);
...

上記のPassiveなイテレータと前セクションのActiveなイテレータとの差異を述べる。
Activeなイテレータのリスティング処理では、イテレーションのループ構造がイテレーションを制御し、ループを通過する度にループ内のオブジェクトはリストより回収され、文字列を出力する。
Passiveなイテレータのリスティング処理では、明確なループ処理は存在しない。単純に、リスト内におけるオブジェクトの処理(─今回の場合は単純な文字列の出力)をforEachメソッドにて呼び出す。
イテレーションの制御はforEachメソッドに依存する。

java.util.ListクラスにおけるforEachメソッドシグネチャ

package java.util;

public interface List<E extends Object> extends Collection<E> {

    /**
     * Performs the given action for each element of the Iterable until all elements have been
     * processed or the action throws an exception.
     */
    public default void forEach(Consumer<? super T> cnsmr) {
        ...
    }

    ...
}

java.util.SetインタフェースクラスにおけるforEachメソッドシグネチャ

package java.util;

public interface Set<E extends Object> extends Collection<E> {

    /**
     * Performs the given action for each element of the Iterable until all elements have been
     * processed or the action throws an exception.
     */
    public default void forEach(Consumer<? super T> cnsmr) {
        ...
    }

    ...
}


java.util.MapインタフェースクラスにおけるforEachメソッドシグネチャ

package java.util;

public interface Map<K extends Object, V extends Object> {

    /**
     * Performs the given action for each entry in this map until all entries have
     * been processed or the action throws an exception.
     */
    public default void forEach(BiConsumer<? super K, ? super V> bc) {
        ...
    }

    ...
}

Upgrade Java SE 7 to Java SE 8 OCP Programmer(1Z0-810)対策 第二章 ラムダ式を使用する事前定義済みの型の使用

第二章よりJava8で追加された関数型インタフェースの各クラス、メソッドの仕様を追っていく。
各インタフェースの詳細まで追うのは辛いので取り扱わない…

■注意
正確な情報は下記リンクを参照してください。

■試験対策ページ
java.boot.by

■試験
Upgrade Java SE 7 to Java SE 8 OCP Programmer | Oracle Certification Exam

2.1. java.util.function パッケージのインタフェースについて説明する

多くの再利用可能な機能要件について、関数型インタフェースとラムダ式は取り込むことができる。
Java8の設計者は一般的なユースケースを取り込み、それらのためのライブラリを実装した。
java.util.functionと呼ばれる新しいパッケージは、一般的なユースケースに対する機能を提供する。

※WARNING※
このセクションで提示するインタフェースのリストは完全ではない。最も重要な部分の関数型メソッドシグネチャのみ列挙する。

java.util.function.Predicate
パラメータの型: T
戻り値の型  : boolean
メソッドの詳細: 1つの引数の述語(boolean値関数)を表す。

package java.util.function;

@FunctionalInterface
public interface Predicate<T extends Object> {

    public boolean test(T t);

}


java.util.function.Consumer
パラメータの型: T
戻り値の型  : void
メソッドの詳細: 単一の入力引数を受け取って結果を返さないオペレーションを表す。

package java.util.function;

@FunctionalInterface
public interface Consumer<T extends Object> {

    public void accept(T t);

}


java.util.function.Function
パラメータの型: T
戻り値の型  : R
メソッドの詳細: 1つの引数を受け取って結果を生成する関数を表す。

package java.util.function;

@FunctionalInterface
public interface Function<T extends Object, R extends Object> {

    public R apply(T t);

}


java.util.function.Supplier
パラメータの型: (なし)
戻り値の型  : T
メソッドの詳細: 結果のサプライヤを表す。サプライヤを呼び出すたびに新規もしくは異なる結果が返される必要はない。

package java.util.function;

@FunctionalInterface
public interface Supplier<T extends Object> {

    public T get();
}


java.util.function.ToDoubleFunction
パラメータの型: T
戻り値の型  : double
メソッドの詳細: double値の結果を生成する関数を表す。これは、Functionに対して、doubleを生成するようプリミティブ化を行ったもの。

package java.util.function;

@FunctionalInterface
public interface ToDoubleFunction<T extends Object> {

    public double applyAsDouble(T t);
}


java.util.function.BiPredicate
パラメータの型: T,U
戻り値の型  : boolean
メソッドの詳細: 2つの引数の述語(boolean値関数)を表す。これは、Predicateを引数を2個取るようにしたもの。

package java.util.function;

@FunctionalInterface
public interface BiPredicate<T extends Object, U extends Object> {

    public boolean test(T t, U u);

}


java.util.function.UnaryOperator
パラメータの型: T
戻り値の型  : T
メソッドの詳細: 単一のオペランドに対する操作に作用して同じ型の結果を生成する演算を表す。これは、オペランドと結果の型が同じである場合においてFunctionを特殊化したもの。
メモ:Functionクラスを拡張しており、Function.apply(Object)メソッドを引き継いでいる。

package java.util.function;

@FunctionalInterface
public interface UnaryOperator<T extends Object> extends Function<T, T> {

}

【参考】
java.util.function (Java Platform SE 8)

Upgrade Java SE 7 to Java SE 8 OCP Programmer(1Z0-810)対策 第一章 ラムダ式(3)

(1)(2)に引き続き、ラムダ式の章を進める。

■試験対策ページ
java.boot.by

■注意
全文が意訳です。正確な情報は下記リンクを参照してください。

■試験
Upgrade Java SE 7 to Java SE 8 OCP Programmer | Oracle Certification Exam

1.3. ラムダ式について説明する 匿名インナークラスを使用したコードをラムダ式を使用するようにリファクタリングする 型推論とターゲット型付けについて説明する

匿名インナークラスとラムダ式で置き換える
開発者のコードの一部には無名インナークラスが含まれており、時として読みづらい。
開発者は匿名インナークラスを、読みやすさとメンテナンスしやすさのために置き換えるべきである。

匿名インナークラスとラムダ式での置き換えによって、定型化したコードの何行かのコードを書くよりも開発時間を短縮できるだろう。
一般的なSwingアプリケーションでは匿名インナークラスをアプリケーションに機能追加する際に必要とする。
例えば匿名クラスはボタンにactionを追加するよく使われる方法である。ここでの問題は内部クラスは読みにくいということであり、多くの定型化したコードを書く必要があることである。

下記のコードはボタンのactionの実装として匿名インナークラスの実装を試している。
まずは下記のラムダ式で置き換える前のコードを見てほしい。

JButton button = ...
JLabel comp = ...

button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        comp.setText("Button has been clicked");
    }
});

例えばActionListenerインタフェースクラスの具象クラスを実装する。このインタフェースはactionPerformedという1つのメソッドを保持している。
actionPerformedメソッドはユーザーが実際に画面上のボタンを押下した時に、buttonインスタンスより呼び出されている。
匿名インナークラスはメソッドの実装を提供する。

匿名インナークラスはJavaプログラマーがコード内でデータを受け渡しやすいように設計されている。
だが不幸なことに、それらはコードをわかりにくくしている。ここでは1行の重要なコードを呼び出すために4行の定型化されたコードが必要となる。

定型化されたコードの問題は1つではない。このコードは開発者の意図が明確でなく非常に読みづらい。
開発者が行いたいことはいくつかの振る舞いを実行するすることであり、(メソッドに)オブジェクトを渡したくない。
Java8ではこのコードをラムダ式で書くことができる。下記にサンプルを示す。

JButton button = ...
JLabel comp = ...

button.addActionListener(e -> comp.setText("Button has been clicked"));

・インタフェースを実装したオブジェクトを渡す代わりに、匿名の関数(ブロック化されたコード)を渡している。
・"e"(Javaの修飾子名以外が指定可能)は匿名インナークラスのパラメータのようなパラメータである。
・"->"はラムダ式の関数部からパラメータを分離する。ユーザがボタンをクリックした際に動作する。
・他の匿名インナークラスを使用していたときとの違いは変数eの宣言である。以前は明確に ActionEvent e と型を宣言していた。
 今回の例(※ラムダ式を使った場合)開発者に型となるクラスが渡されていないが、このクラスはコンパイルすることができる。
 内部で何が起こっているかというと、変数eに対してaddActionListenerメソッドシグネチャより型推論が行われている。
 これは、開発者は明確にパラメータの型を記述する必要がないということを意味する。

※WARNING※
いくつかの場面ではJavaコンパイラが型を推論できない場合、変数の型やパラメータの型を明確に記述する必要がある。

下記の例はラムダ式の引数に対して、明示的に型を記述する場合。

JButton button = ...
JLabel comp = ...

button.addActionListener((ActionEvent e) -> comp.setText("Button has been clicked"));

ラムダ式の構文
ラムダ式は下記の構文から構成される。

  • 括弧で囲まれたカンマ区切りのリスト

メモ:ラムダ式のパラメータの型は省略することができる。加えて、パラメータがひとつの場合は括弧を省略することができる。例えば下記のラムダ式は正しい。

s -> s.getAge() >= 18
(Student s) -> s.getAge() >= 18
(s) -> s.getAge() >= 18
  • メソッド本文は「1つの式」もしくは「ブロック」から構成される。
... -> s.getAge() >= 18

1つの式しか記述しない場合、Javaの実行環境は式を戻り値の評価を行う。代わりに「return」句を使うことができる。

... -> { return s.getAge() >= 18; }

return句に型はない。ラムダ式では、式を中括弧{}で囲む必要がある。しかし、voidメソッドは中括弧で囲む必要はない。例えば、下記のラムダ式は正しい。

email -> System.out.println(email)

Upgrade Java SE 7 to Java SE 8 OCP Programmer(1Z0-810)対策 第一章 ラムダ式(2)

(1)に引き続き、ラムダ式の章を進める。

■注意
全文が意訳です。正確な情報は下記リンクを参照してください。

■試験対策ページ
java.boot.by

■試験
Upgrade Java SE 7 to Java SE 8 OCP Programmer | Oracle Certification Exam

1.2. 関数型インタフェースクラスの実装

関数型インタフェースクラスはラムダ式メソッド参照のための型を提供する。
各関数型インタフェースクラスは関数メソッドと呼ばれる単一の抽象メソッド(single abstract method (SAM))を持ち、ラムダ式のパラメータと戻り値に対応あるいは合致する。
JDK8において、既に存在するRunnabl、Callable、 Comparator、ActionListenerのような、単一のメソッドを持つインタフェースクラスは、現在の関数型インタフェースとラムダ式となる。これらは単一の抽象メソッドが使われているインタフェースクラスにて使用することができる。


@FunctionalInterface アノテーション
Java8では@FunctionalInterfaceという新しいアノテーションを関数型インタフェースに付与するよう紹介している。関数型インタフェースであるという意図の伝達とコンパイラにいくつかの追加のチェックを許可するためである。

※WARNING※
@FunctionalInterfaceはオプションである。Javaコンパイラに対するヒント句である。

例えば下記のインタフェースはコンパイルに成功する。

public interface MyInterface {
}

しかし関数型インタフェースであることを示した場合(コンパイルに失敗する)

// Java 8 compiler fails
@FunctionalInterface
public interface MyInterface {
}

抽象メソッドが無かった場合、コンパイラは"MyInterface is not a functional interface" や "no abstract method was found"といったエラーを発生させる。
2つ目のメソッドを追加した場合もエラーを発生させる。

// Java 8 compiler fails !
@FunctionalInterface
public interface MyInterface {
    void doIt();
    void doItNow();
}

インタフェースにおけるデフォルトメソッド
Java8のインタフェースクラスではデフォルトメソッドとstaticメソッドをサポートする。
デフォルトメソッドインスタンスメソッドの先頭に「default」キーワードを付けて定義する。またデフォルトメソッドはコードの本文を提供する。
デフォルトメソッドを含むインタフェースの具象クラスは、デフォルトメソッドとその他のメソッドをオーバーライドすることができる。
関数型インタフェースクラスは単一の抽象メソッドを必要とする。下記のコードはコンパイルに失敗する、無効な関数型インタフェースクラスである。

// Java 8 compiler fails !
@FunctionalInterface
public interface MyDefInterface {
    default void doIt() { /* cool implementation */ }
}


しかし下記の関数型インタフェースクラスはコンパイルに成功する。

@FunctionalInterface
public interface MyDefInterface {
    default void doIt() { /* cool implementation */ }
    void doItNow(); // Single Abstract Method (SAM)
}

インタフェースにおけるstaticメソッド
staticメソッドは定義したクラスと結びついている、というよりもクラスのどのオブジェクトでも使用できる。
各クラスのインスタンスは、クラス内でstaticメソッドを共有している。
Java8ではインタフェースに定義したstaticメソッドをデフォルトメソッドで補足することができる。

staticメソッドのようにインタフェースにstaticメソッドを定義したい時はメソッドシグネチャの先頭に「static」を付与する。
インタフェースクラスの全てのメソッド宣言は「public」となるので「public」キーワードは省略することができる。

staticメソッドを含むインタフェースクラスを実装したい時、staticメソッドはまだインタフェースクラスの一部であり、実装クラスの一部ではない。
理由はクラス名をメソッドの接頭辞に付加できないため。staticメソッドの呼び出し時はインタフェースクラス名を付記する必要がある。

関数型インタフェースは単一の抽象メソッドを定義する必要がある。下記のコードはコンパイルに失敗する、無効な関数型インタフェースである。

// Java 8 compiler fails !
@FunctionalInterface
public interface MyStatInterface {
    static void doIt() { /* cool implementation */ }
}

しかし下記の関数型インタフェースはコンパイルに成功する。

@FunctionalInterface
public interface MyStatInterface {
    static void doIt() { /* cool implementation */ }
    void doItNow();
}

java.lang.Objectクラスの関数型インタフェース
インタフェースクラスがjava.lang.Objectクラスの抽象メソッドのひとつをオーバーライドした場合、関数型インタフェースの抽象メソッドとしてカウントしない。
例えば java.util.Comparatorは関数型インタフェースであるが、2つの抽象メソッドを有している。

package java.util;

@FunctionalInterface
public interface Comparator<T> {

    int compare(T o1, T o2);

    boolean equals(Object obj);

    ...
}

(関数型インタフェースである)理由は、equals() メソッドjava.lang.Objectクラスの持つpublicメソッドシグネチャと一致するため。

【参考】
Java関数型インターフェースメモ(Hishidama's Java8 Functional Interface Memo)