オブジェクトにあるプロパティが存在するかをチェックする

手法

JSのオブジェクトを扱う際に、あるプロパティが存在するかをチェックする作業はしばしば発生します。実は、この一見簡単そうな作業を実施する為に、いくつかのやり方が存在します。考え方で分けて見れば、以下の2種類になるかと思います。

該当プロパティの値をチェックする

よく見かける手法はundefined !== obj[prop]もしくはundefined !== obj.propでしょう(obj = オブジェクト、prop = プロパティ)。まずオブジェクトからプロパティを出して値をみます。もしオブジェクトに該当プロパティが存在しなければ、値はundefinedになりますので、この特性を利用する手法ですね。
この手法の異種として、void(0) !== obj[prop]があります。void演算子が分かればすぐ理解できるでしょう。void(0)の戻り値がundefinedになるからですね。
また、'undefined' !== typeof obj[prop]という書き方もあります。値の型を取得し、undefined型であるかを判別する考えです。
更に、もっとお洒落な書き方も存在します。!!obj[prop]です。下記のように書くことが可能になります。

1
if(!!obj.prop) return 'OK';

エクスクラメーションマークを2つ先頭に追加することで、結果の反転を二回行います。「False→True→False」みたいに、元の結果に戻すやり方ですね。

undefined !== obj[prop]がもっと直感的なのに、なぜわざわざvoid(0) !== obj[prop]'undefined' !== typeof obj[prop]を使うか、疑問を感じたので、ベンチマックを行ってみました。実は後二者の効率は前者の3倍程になります。なるほど〜
あともう一つ気を付けなければいけないのは元々プロパティの値はundefinedになっているケースです。上記の手法を使ってしまったら、該当プロパティが存在していても、falseが出てしまいます。
特に!!obj[prop]を使用する時、もっと注意を払わなければなりません。0nullの反転結果はtrueになるから、該当プロパティに入る値の型を100%把握していなければ、使用するのをやめておいた方が安全でしょう。

該当プロパティの存在を確認する

抽出を行わず、直接チェックする方法もあります。inもしくはhasOwnPropertyの使用です。

1
2
3
4
5
// in
if(prop in obj) return 'OK';

// Object.prototype.hasOwnProperty
if(obj.hasOwnProperty(prop)) return 'OK';

Read More

OptionalのorElseとorElseGet

最近OptionalのorElseorElseGetの区別は社内でちょっと話題になっていました。これらは同じように見えますが、使い方に気をつけなければハマってしまうかもしれません。まあ、まずJavaDocをみてみましょう。

定義

オラクルのドキュメントの中にorElseの定義は下記のように書かれています。

public T orElse(T other)
存在する場合は値を返し、それ以外の場合はotherを返します。

パラメータ:
other - 存在する値がない場合に返される値、nullも可

戻り値:
値(存在する場合)、それ以外の場合はother

一方、orElseGetこんな感じです。

public T orElseGet(Supplier<? extends T> other)
値が存在する場合はその値を返し、そうでない場合はotherを呼び出し、その呼び出しの結果を返します。

パラメータ:
other - Supplier(値が存在しない場合は、これの結果が返される)

戻り値:
値(存在する場合)、それ以外の場合はother.get()の結果

例外:
NullPointerException - 値が存在せずotherがnullの場合

やはり説明も似ていますね。でも、nullについての説明は違います。前者の説明の中に、nullも可が入っていますが、後者にはそれがなく、逆にNullPointerExceptionという例外の説明が入っています。すなわち、Optional.ofNullable(null).orElse(null)はnullを返してくれますが、Optional.ofNullable(null).orElseGet(null)は例外をスローしてしまいます。
それはそうでしょう。引数は確実に違うからですね。orElseの引数は値で、orElseGetはSupplierというラムダ式を取っています。Supplierのgetメソッドに通じて値を取得するので、Supplier自体がnullになってしまったら、NullPointerExceptionがスローされてもおかしくありません。

遅延実行

でしたら、下記のコードの実施結果は同じように思えますよね。

1
2
3
4
5
6
7
8
9
10
11
public class MyTest1 {
private final static Supplier<String> test = () -> {
final String result = "test";
System.out.print(result);
return result;
};

public static void main(String[] args) {
System.out.print(Optional.of("test").orElseGet(test));
}
}

Read More

「Vue.js Tokyo v-meetup #6」参加レポート

Vue.js Tokyo v-meetup #6

12月13日に開催された「Vue.js Tokyo v-meetup #6」に参加してきました。前回のMeetupがVue.jsを用いたサーバーサイドレンダリングの魅力を見せてくれたと言えるなら、今回のMeetupは2017年、Vue.jsのエコシステムがますます繁栄してきたアピールになった年と言えるのではないでしょうか。開発環境、テストツール、コーディング規約、UIフレームワーク、強力な公式サポートのおかげでWebのフロントエンド開発における欠かせないものを全部用意してくれています。一年前と比べて、今のVue.jsはすでにフロントエンドのギークたちのオモチャから、プロダクションレディで成熟したフレームワークになってきていると実感しています。

TypeScriptサポート

TypeScript + Vue.js

Vue.jsのバージョン2.4まで、Vue.jsにおけるTypeScriptのサポートは不足していました。TypeScriptでVue.jsプロジェクトを作成すること自体は問題ないですが、型定義の恩恵は受けにくい状況だったように感じます。@maeharinさんが発表の中で紹介して頂いたように、Vue.js2.5以前、object型構文の内ではthisの型推論はできませんでした。そもそも、TypeScriptはこのような推論をサポートしていませんでしたので、Vue.js側のサポートも厳しかったでしょう。とは言え、Vue.jsの場合、「vue-class-component」を利用しない限り、object型構文は基本なので、かなり致命的な問題だったと考えられます。めでたいことに、TypeScriptのプルリクエスト#14141によって、objectリテラル内のthisの型推論が可能になりました。そして、Vue.jsのプルリクエスト#6391において、型定義の追加を含み、TypeScript向けの改善が行われた為、バージョン2.5以降、Vue.js+TypeScript開発が一気に便利になってきました。例えば、プロパティの補完とタイプミスを避けるために、@maeharinさんがプロジェクトの中にAPIの型定義を入れました。swagger-codegenというツールを使い、RubyクライアントとTypeScriptの定義を自動生成できました。自動生成されたTypeScriptの型定義はフロントの開発で利用されていますので、非常に効率の良い開発を行ったようです。ちなみに、swagger-codegenで使用されるAPI定義ファイルもSpringFoxによって自動生成されているため、無駄な重複作業がかなり排除できたんじゃないかと思います。

Visual Studio Code + Vetur

Visual Studio Code

Web開発を行う際に、使いやすい開発環境は非常に大事な要素になっています。Vue.js0.1Xの時代からAtomやSublime Text上のプラグインがすでに提供されて来ましたが、自動補完が足りなかったり、Lintができなかったり、とても使いやすいとは言えないでしょう。しかし、これらの問題はVSCodeの素早い進化とプラグインVeturの登場によって解消されました。今回の会場提供者でもある、マイクロソフトの井上さんがデモの中に見せて頂いたように、VSCode+VeturコンビでVue.jsの開発を行えば、デバッグを含み、IDE並の開発体験ができるはずです。VSCodeがデフォルトでサポートしているEmmetと前述のTypeScript(実は、マイクロソフトはTypeScript-Vue-Starterというテンプレートを提供していますよ)を使えば、さらにスムーズな開発が期待できますね。

UIフレームワーク

Element UI

Read More

Vue.jsでプレースホルダを用いたローディングを実装する際のトランジション

この記事の実装例はこちらです。

背景

UX向上の観点により、読み込みが遅いもの(特に大きなイメージファイルなど)が存在する場合、先に枠を表示し、ダウンロードできた後本物に置き換える設計は最近増えてきています。そのうち、下の図で示されているようなFacebookのロード画面は好例の一つだと言えるでしょう。

Facebookローディング画面のプレスホルダー

この設計をVue.jsで実装するのはそれほど難しくないでしょう。いくつかのVue.js製のプレスホルダーはすでに公開されている(その一その二)ため、それらを条件付きレンダリング(Conditional Rendering)と合わせ、ディレクティブv-ifv-elseを活用するだけで実現可能です。

1
2
<placeholder v-if="content === null" />
<my-content v-else />

こういう風に実装してもダメとは言えませんが、コンテンツの切り替えにトランザクションが全く存在しないため、唐突の感は免れません。よって、置き換えが発生した際に、フェードイン/フェードアウト効果を追加した方がUX的に優しいと思われます。

トランジションモード

Vue.jsでトランジションを追加するのに、非常に簡単なやり方が存在します。そう、Transitionというラッパーコンポーネントで囲み、CSSで定義する方法です。

トランジションの状態定義

Read More

Hexoブログのカスタムホームページの作り方

背景

デフォルトの設定でHexoのブログを作っている場合、ホームページ(index)がすでに存在しています。かなり便利でありがたい機能ですが、カスタマイズしようとしたら、手数がやや掛ってしまいます。
具体的に言うと、テーマ内のindex.ejsファイルに記入されているコードはlayout.ejs<%- body %>に入ります。すなわち、indexページのレイアウトはindex.ejsのほか、layoutフォルダ内のlayout.ejsにも左右されます。別に悪い仕様とは全然言えませんが、ホームページだけは通常のテンプレートに適用されたくないというユースケースもあるでしょう。
それを回避するために、HexoのGitHub上のIssueを調査したり、何回か試したりして、ようやく分かりましたので、ここで整理してみたいと思います。

手順

  1. hexo-generator-indexというプラグインを削除します。自分でインデックスページを作るので、当然ながら自動生成の機能は不要になります。Hexoプロジェクトのルートディレクトリに行って、npm uninstall hexo-generator-indexを打てば問題ないはずです。
  2. sourceフォルダーの中でindex.mdというファイルを作ります。こちらのファイルはエントリーポイントになります。
  3. layout.ejs内に以下のような条件分岐を加えます。
1
2
3
4
5
<% if (page.source == 'index.md') { %>
<span>こちらはホームページのテンプレート</span>
<% } else{ %>
<span>こちらはその他のページのテンプレート</span>
<% } %>

参考リンク

Read More

ポリモーフィズムクイズ

問題

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class MyTest
{
private static class A {
String message = "hello";
Consumer<String> hello = s -> System.out.print(message + s);
void hello(String s) { this.hello.accept(s); }
}

private static class B extends A {
String message = "ハロー";
void hello(String s) { this.hello.accept(s); }
}

public static void main(String[] args) {
A b = (A) new B();
b.hello(" world");
System.out.print("/");
b.hello.accept(" world");
}
}

上記のコードの実行結果は

  • A. ハロー world/hello world
  • B. ハロー world/ハロー world
  • C. hello world/ハロー world
  • D. hello world/hello world
  • E. コンパイルエラー

正解

答えは

  • D. hello world/hello world

説明

一見複雑そうですが、実はポリモーフィズムをちゃんと理解できれば、すぐ解けるはずです。それでは、mainメソッドの処理を追って見ましょう。

  • mainメソッドの一行目ではAB実装の変数bを宣言しました。(A)というアップキャストの処理が書かれていますが、実はコンパイラが自動的に行ってくれる作業なので、ただの目障りで意味がありません(勿論エラーにもなりませんが・・)。
  • bのメソッドを利用する際に、コンパイラはまず実装型にそのメソッドがあるかどうかを見ます(二行目)。クラスBhelloというメソッドがありましたね。オーバーライドが明記されていませんが、シグネチャに違いがない為、親クラスのhelloメソッドがオーバーライドされていることは分かります。ポリモーフィズムのルールによりクラスBhelloメソッドが実行されます。
  • helloいうメソッドはhelloというコンシューマー関数のフィールドを呼んでいますが、残念ながらAクラスにしかhelloが存在しません。その為、やむを得ずAクラス内のhello関数を利用することになります。(ちなみに、メソッド名とフィールド名の重複は許されますので、コンパイルエラーになりません)
  • helloという関数はmessageというフィールドを使います。クラスAmessagehelloですので、出力結果はhello worldになります。クラスBにもmessageというフィールドが存在しますが、今使っているhello関数とは無関係ですね。
  • さらにmainの四行目ですが、bオブジェクトのhello関数を利用しているように見えますね。しかし、フィールド変数のオーバーライドが存在しないことを忘れてはいけません。hello関数はhelloメソッドと同じく処理手続きを定義するものですが、本質的にはmessageのようなフィールド変数です。その為、どのフィールドが利用されるかは宣言時の参照型によります。Aとして宣言された為、クラスAhello関数が利用され、二行目の出力結果と同じようになります。

Read More

try-catch-finally、try-with-resources、そして発生した例外たち

問題

下記のコードの出力内容はどんな感じでしょうか?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class Test {
public static void main (String[] args) throws Exception {
try {
run();
} catch(Exception e) {
printSuppressedExceptions(e, "-");
}
}

public static void printSuppressedExceptions(Throwable t, String s) {
System.out.println(s + t + " suppressed " + t.getSuppressed().length + " exception/exceptions.");
if(t.getSuppressed().length != 0) {
for (Throwable ts : t.getSuppressed()) {
System.out.println(s + ts + "[ is suppressed by ]" + t);
printSuppressedExceptions(ts, s + s);
}
}
}

public static String run() throws Exception {
try(MyResource r = new MyResource("resource 1");
MyResource r2 = new MyResource("resource 2");
) {
return System.out.printf("return from try\n").append("",1,2).toString();
} catch(Exception e) {
printSuppressedExceptions(e, "*");
throw e;
} catch(Throwable t) {
System.out.println("print from 2nd catch block");
return "return from 2nd catch block";
} finally {
new MyResource("resource 3").close();
new MyResource("resource 4").close();
}
}
}

class MyResource implements AutoCloseable {
private final String name;

public MyResource(String name) {
this.name = name;
}

@Override
public void close() throws Exception {
throw new Exception("exception" + " from " + this.name);
}
}

分析

これを回答するのに、以下の内容を把握する必要があります。

1.System.out.printf(String)に戻り値が存在します?

Javadocを調べれば分かりますが、PrintStream#printfPrintStream#printPrintStream#printlnと違い、戻り値はPrintStreamになっています。
一見あんまり意味がなさそうですが、コンソールへの出力が可能でありながらとして扱える為、としてしか扱えないSystem.out.printlnなんかより用途が多いはずです。

1
2
3
4
5
6
7
8
9
class Test
{
public static void main (String[] args)
{
if(System.out.printf("hello, ") != null) {
System.out.println("world");
}
}
}

例えば、上記のコードの出力値はHello, worldになりますね。

そして、PrintStream型である為、appendメソッドを使用することが可能です。しかし、二つ目の引数と3つ目の引数の大きさに気をつけなければ、お馴染みのStringBuilderと同じく、StringIndexOutOfBoundsExceptionが発生してしまうかもしれません。

Read More

コンポーネント間イベント通信の方法を変更しよう

背景

今までVue.jsのコンポーネント間のイベント通信を行わせる時に、$dispatch()$broadcast()を用いたことが多いです(Vue.js及びコンポーネントについては公式ページをご参考ください)。しかし、これらはVue.js 2.0で廃止される予定です^1。コンポーネントのツリー構造の膨大化によって追跡が難しくなることと兄弟コンポーネントの通信がやりにくいことが理由として挙げられています。その代わりに、Node.jsのEventEmitterパターンが推奨されています^2。このやり方はVue.js 1.0のAPIを使って実現できます^3ので、まだ2.0に移行していなくても、これからイベント通信を書く時に使用すべきでしょう。

移行の手順

下記のコードを2.0の書き方に移行するとしましょう。

1
2
3
4
5
6
7
<div id="app">
<div @click="clicked">
{{ text }}
</div>
<comp-a></comp-a>
<comp-b></comp-b>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
var CompA = Vue.extend({
template: '<div @click="clicked">{{text}}</div>',
data() {
return {
name: 'Component A',
text: 'Component A'
};
},
methods: {
clicked() {
console.log('component a clicked');
this.$dispatch('DISPATCHED', this.name);
}
},
events: {
BROADCASTED(name) {
console.log(name);
this.text = 'broadcasted from ' + name;
}
}
});

var CompB = Vue.extend({
template: '<div @click="clicked">{{text}}</div>',
data() {
return {
name: 'Component B',
text: 'Component B'
};
},
methods: {
clicked() {
console.log('component b clicked');
this.$dispatch('DISPATCHED', this.name);
}
},
events: {
BROADCASTED(name) {
console.log(name);
this.text = 'broadcasted from ' + name;
}
}
});

var App = new Vue({
el: '#app',
components: {
CompA, CompB
},
data: {
name: 'app',
text: 'app'
},
methods: {
clicked() {
console.log('app clicked');
this.$broadcast('BROADCASTED', this.name);
}
},
events: {
DISPATCHED(name) {
console.log(name);
this.text = 'dispatched from ' + name;
}
}
});

1. まず、新しいVueインスタンスを作成し、イベントハブの役割を担わせます。

1
var bus = new Vue();

2. 送信側は$dispatch$broadcastを使用してイベントを発信していましたが、イベントハブに一旦送信するので、全てを$emitに書き換えます。

1
2
3
4
// old
this.$dispatch('DISPATCHED', this.name);
// new
bus.$emit('DISPATCHED', this.name);

Read More

ラムダ式で3つ以上の引数に対応しよう

引数が2つ以下の場合

Java8既存のFunctionインターフェースを使えば、うまく対応できます。
例:

1
2
3
4
5
6
7
// 引数が1つしかない
Function<String, String> func1 = (str1) -> str1+"!";
System.out.println(func1.apply("Aha")); //Aha!

// 引数が2つしかない
BiFunction<String, String, String> combineStrings = (str1, str2) -> str1+str2;
System.out.println(combineStrings.apply("Hello ", "World")); //Hello World

なお、既存のFunctionインターフェースを分かりやすくまとめてくれた図表を見つけましたので、転載します

Java 8 Functional Interface Naming Guide

引数が3つ以上の場合

解決法1: 自作関数型インターフェース

Java8のFunctionインターフェースにはインプットが3つ以上のインターフェースが提供されていません。
ならば、Functionインターフェースを自作してみましょう。
実は、ありがたいことに、Java8がただ単純にインプットが3つ以上の関数型インターフェースを提供してないだけで、(少なくてもOracleとOpenJDKのJavaでは)複数の引数へ対応する準備が整っています。どういうことでしょうか?まず、BiFunctionのソースを見てみましょう。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

@FunctionalInterface
public interface BiFunction<T, U, R> {

/**
* Applies this function to the given arguments.
*
* @param t the first function argument
* @param u the second function argument
* @return the function result
*/
R apply(T t, U u);

/**
* Returns a composed function that first applies this function to
* its input, and then applies the {@code after} function to the result.
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
*
* @param <V> the type of output of the {@code after} function, and of the
* composed function
* @param after the function to apply after this function is applied
* @return a composed function that first applies this function and then
* applies the {@code after} function
* @throws NullPointerException if after is null
*/
default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t, U u) -> after.apply(apply(t, u));
}
}

肝心なのはR apply(T t, U u);だけです。このapply関数がjava.lang.reflect.Proxy.KeyFactory#applyで実装されています。

Read More