gitでmergeした時にconflictして、やっぱやめたってなる時に今までは git reset --hard HEAD
ってしてたんだけど、ふとmanを眺めたら --abort
オプションがあるのを発見した。
rebase
とか cherry-pick
にあるのになんでないのって思ってたけど、どうも勘違いだったっぽい。
gitちゃんと使うならもっとオプション知らないといけないなぁと思った。
gitでmergeした時にconflictして、やっぱやめたってなる時に今までは git reset --hard HEAD
ってしてたんだけど、ふとmanを眺めたら --abort
オプションがあるのを発見した。
rebase
とか cherry-pick
にあるのになんでないのって思ってたけど、どうも勘違いだったっぽい。
gitちゃんと使うならもっとオプション知らないといけないなぁと思った。
以前TODOコメントにGitのbranch名を入れるためにSnippet書いたという記事を書いた。
この記事以降もチームは変わってないし使っているのだが、branch名そのままだとそのbranchのmergeまでに終わらせないといけないTODOに思えるという指摘があった。
たしかにそうだなって感じだったので、branch名のルールを少し見直して feature-name/part
みたいな感じにすることにした。
それに合わせてsnippetのコードも少し改変。
snippet todo options head # TODO(`substitute(substitute(substitute(system('git rev-parse --abbrev-ref HEAD'), '\n\+$', '', ''), '^\(feature|hotfix|release|design\)\/', '', ''), '\/.\+$', '', '')`): ${0}
git-flow で使いそうなprefixとか design/
とか を取っ払ってやることでとにかくそれっぽい名前を出すことに成功した。
最近PerlをAtomで書けるように準備しているので、このsnippetもそのうち使わなくなりそうだけども、とりあえず書き残しておく。
これは Kotlin Advent Calendar 2016 9日目の記事です。
今日はKotlinの演算子オーバーロードについて紹介します。
演算を行うための記号が演算子です、だいたいのブログラミング言語にはありますよね?
+ - / * =
や && ||
など、プログラミング言語にはたくさんの演算子があります。
Kotlinでは様々なクラスを演算子で演算した時の挙動をコントロールする演算子オーバーロードの機能があります。
これはJavaにはない機能ですね。
Kotlinで演算子により演算を行う場合、左辺のクラスに存在する演算子に対応した名前で右辺の型にマッチするメソッドが存在した場合に行われます。
1 + 2 // 3
+
は plus
メソッドを呼ぶので以下と同じ意味になります。
1.plus(2)
実際にKotlinで数値リテラルのメソッドを呼ぼうとすると、補完候補に plus
や他の演算子に対応したメソッドが出てきます。
決まった名前でメソッドを作るだけです。これは通常のメソッド以外に拡張関数でも可能です。
私はボウリングが趣味なので、ボウリングのスコアを表現するクラスを作ります。
ボウリングは雑に説明すると、10本のピンに向かってボールを転がしていっぱい倒したらハッピーというスポーツです。
10本のピンは2回以内に倒しきらないといけなくて、1投目で倒しきったらストライク、2投目で倒しきったらスペア、倒しきれなかったらミス(エラー)となります。
ストライク、スペア、ミスのどれかで投げ終わる1つの流れをフレームといい、これを9回繰り返します。
9回繰り返した次の10フレーム目は最大3投でき、3投するかミスをした場合に終わります。
この10フレームが1ゲームになります。
まずはスコアそのものを表現するクラスを作りましょう。
object Strike : BaseScore(value = 10) object Spare : BaseScore(value = 0) class Score(value: Int) : BaseScore(value = value) // ↓複雑になるので一旦は考慮しないことにします // object Foul : Score(value = 0) // object Miss : BaseScore(value = 0) // object Gutter : BaseScore(value = 0) // class Split(value: Int) : Score(value = value) }
スペアは10-(1投目の投数)ですが、便宜的にオブジェクトで表現します。
次にフレームを作ります、9フレーム目までのフレームは1〜2つのスコアからなります
複雑度を下げるために、フレームは完了しないと計算できないようにします。
sealed class Frame(val scores: List<BaseScore>) { object StrikeFrame : Frame(listOf(Strike)) class SpareFrame(firstScore: BaseScore) : Frame(listOf(firstScore, Spare)) class MissFrame(firstScore: BaseScore, secondScore: BaseScore) : Frame(listOf(firstScore, secondScore)) } class TenFrame private constructor(val scores:List<BaseScore>) { constructor(firstStrike: Strike, secondStrike: Strike, thirdScore: BaseScore) : this(listOf(firstStrike, secondStrike, thirdScore)) constructor(strike: Strike, secondScore: Score, thirdScore: BaseScore) : this(listOf(strike, secondScore, thirdScore)) constructor(firstScore: BaseScore, spare: Spare, thirdScore: BaseScore) : this(listOf(firstScore, spare, thirdScore)) constructor(firstScore: BaseScore, missScore: Score) : this(listOf(firstScore, missScore)) }
10フレーム目だけ途中で投げ終わる可能性があるので、起こり得る全パターン(ただし3投目は何でもいいので固定)をコンストラクターで用意しました。
フレームが集まればゲームになります、ゲームを表現するクラスを作りましょう。
sealed class Game(val frames: List<Frame>) { class IncompletedGame(frames: List<Frame>) : Game(frames) class CompletedGame(frames: List<Frame>, tenFrame: TenFrame) : Game(frames) }
本当は初期家事にフレーム数のバリデーションが必要ですが、今回は雑にこういう感じでいきます。
さて、これを演算できるように実装していきましょう。
Score(1) + Score(8) // ミス Score(1) + Spare // スペア
まずは↑のパターンを作ります
open class Score(value: Int) : BaseScore(value = value) { open operator fun plus(b: BaseScore): MissFrame { return if (this.value + b.value < 10) { MissFrame(this, b) } else { error("Invalid score combination!") } } operator fun plus(spare: Spare): SpareFrame { return SpareFrame(this) } }
この組み合わせで1つのフレームができあがります。
ストライクだけは1つでフレームが終わってしまうので、ちょっと特殊です
Strike + Score(3) // ストライクと3ピン倒した状態、今回これは考慮しない Strike + Strike // ダブル Strike + (Score(3) + Spare) // ストライクとスペア
これは Strike オブジェクトに演算子オーバーロードをしましょう。
object Strike : BaseScore(value = 10) { operator fun plus(strike: Strike): IncompletedGame { return IncompletedGame(listOf(StrikeFrame, StrikeFrame)) } operator fun plus(frame: Frame) : IncompletedGame { return IncompletedGame(listOf(StrikeFrame, frame)) } }
これだけで大丈夫です、Strikeは1つでフレームになるので、演算時にStrikeFrameに暗黙的に変換しているような扱いにしています。
フレームが2つ以上集まると完了していないゲームになりますね。ストライク単体もフレームと同じ扱いです。
sealed class Frame(val scores: List<BaseScore>) { operator fun plus(b: Frame): IncompletedGame { return IncompletedGame(listOf(this, b)) } operator fun plus(strike: Strike): IncompletedGame { return IncompletedGame(listOf(this, StrikeFrame)) } }
単純ですね。
完了していないゲームにどんどんフレームを足していきます、最終的には9つまで許容します。
class IncompletedGame(frames: List<Frame>) : Game(frames) { operator fun plus(b: Frame): IncompletedGame { return if (this.frames.size == 9) { error("Next frame is TenFrame!") } else { IncompletedGame(frames + b) } } operator fun plus(strike: Strike): IncompletedGame { return this + StrikeFrame } }
10個目を足そうとするとエラーになります。
ここまでくるとほぼ完成ですね、10フレーム目を足しましょう。
class IncompletedGame(frames: List<Frame>) : Game(frames) { operator fun plus(b: TenFrame): CompletedGame { return if (this.frames.size != 9) { error("Next frame is not TenFrame!") } else { CompletedGame(frames, b) } } }
ここまできたら、1ゲームを表現する演算ができるようになるはずです。
実際に先日投げたこの3ゲームをKotlinで表現してみましょう。
3ゲーム目ェ… pic.twitter.com/KaIFZVj48G
— たくじ@ToSジェミナ鯖 (@takuji31) December 6, 2016
1ゲーム目
Strike + Strike + (Score(9) + Score(0)) + (Score(7) + Spare) + Strike + (Score(9) + Spare) + (Score(7) + Score(1)) + Strike + Strike + TenFrame(Strike, Score(8))
2ゲーム目
(Score(7) + Spare) + Strike + (Score(9) + Spare) + Strike + (Score(9) + Spare) + (Score(9) + Score(0)) + (Score(9) + Spare) + (Score(9) + Spare) + (Score(8) + Spare) + TenFrame(Score(9), Spare, Score(7))
3ゲーム目
Strike + (Score(9) + Spare) + Strike + (Score(7) + Score(0)) + (Score(9) + Score(0)) + (Score(8) + Score(0)) + Strike + Strike + (Score(7) + Score(1)) + TenFrame(Strike, Score(9), Spare)
全部エラーなしに書けました!
実際のところ、入力のバリデーションが必要だったりするので、これで完成ではないですが、演算子オーバーロードとしてはこれで完成かなと思います。
いかがでしたでしょうか、当たり前ですが右辺の型で戻す型を変えたりなどできるので、結構柔軟な計算ができるようになります。
これ以外にも、中置関数を使うと組み込み演算子以外のカスタム演算子として使うことができるようになります。
詳しくは公式ドキュメントをご覧ください。
計算まで終わらなかったのでそのうち実装して公開します。
以下が今回のコードの全容です。
sealed class BaseScore constructor(val value: Int) { object Strike : BaseScore(value = 10) { operator fun plus(strike: Strike): IncompletedGame { return IncompletedGame(listOf(StrikeFrame, StrikeFrame)) } operator fun plus(frame: Frame) : IncompletedGame { return IncompletedGame(listOf(StrikeFrame, frame)) } } object Spare : BaseScore(value = 0) class Score(value: Int) : BaseScore(value = value) { operator fun plus(b: Score): MissFrame { return if (this.value + b.value < 10) { MissFrame(this, b) } else { error("Invalid score combination!") } } operator fun plus(spare: Spare): SpareFrame { return SpareFrame(this) } } // object Foul : Score(value = 0) // object Miss : BaseScore(value = 0) // object Gutter : BaseScore(value = 0) // class Split(value: Int) : Score(value = value) } sealed class Frame(val scores: List<BaseScore>) { operator fun plus(b: Frame): IncompletedGame { return IncompletedGame(listOf(this, b)) } operator fun plus(strike: Strike): IncompletedGame { return IncompletedGame(listOf(this, StrikeFrame)) } object StrikeFrame : Frame(listOf(Strike)) class SpareFrame(firstScore: BaseScore) : Frame(listOf(firstScore, Spare)) class MissFrame(firstScore: BaseScore, secondScore: BaseScore) : Frame(listOf(firstScore, secondScore)) } class TenFrame private constructor(val scores:List<BaseScore>) { constructor(firstStrike: Strike, secondStrike: Strike, thirdScore: BaseScore) : this(listOf(firstStrike, secondStrike, thirdScore)) constructor(strike: Strike, secondScore: Score, thirdScore: BaseScore) : this(listOf(strike, secondScore, thirdScore)) constructor(firstScore: BaseScore, spare: Spare, thirdScore: BaseScore) : this(listOf(firstScore, spare, thirdScore)) constructor(firstScore: BaseScore, missScore: Score) : this(listOf(firstScore, missScore)) } sealed class Game(val frames: List<Frame>) { class IncompletedGame(frames: List<Frame>) : Game(frames) { operator fun plus(b: Frame): IncompletedGame { return if (this.frames.size == 9) { error("Next frame is TenFrame!") } else { IncompletedGame(frames + b) } } operator fun plus(b: TenFrame): CompletedGame { return if (this.frames.size != 9) { error("Next frame is not TenFrame!") } else { CompletedGame(frames, b) } } operator fun plus(strike: Strike): IncompletedGame { return this + StrikeFrame } } class CompletedGame(frames: List<Frame>, val tenFrame: TenFrame) : Game(frames) } class Main { companion object { fun main(args: Array<String>) { Score(1) + Score(8) // ミス Score(0) + Score(10) // スペア Score(2) + Score(8) // スペア Strike + Strike + (Score(9) + Score(0)) + (Score(7) + Spare) + Strike + (Score(9) + Spare) + (Score(7) + Score(1)) + Strike + Strike + TenFrame(Strike, Score(8)) (Score(7) + Spare) + Strike + (Score(9) + Spare) + Strike + (Score(9) + Spare) + (Score(9) + Score(0)) + (Score(9) + Spare) + (Score(9) + Spare) + (Score(8) + Spare) + TenFrame(Score(9), Spare, Score(7)) Strike + (Score(9) + Spare) + Strike + (Score(7) + Score(0)) + (Score(9) + Score(0)) + (Score(8) + Score(0)) + Strike + Strike + (Score(7) + Score(1)) + TenFrame(Strike, Score(9), Spare) } } }
これは 転職(その2) Advent Calendar 2016 2日目の記事です。
こんにちは、株式会社はてなでアプリケーションエンジニアをやっているid:takuji31です。
最初はそろそろ転職して1年になるのでその辺の話を書こうかと思ったんですが、ちょっと思うところがあって内容を変えました、理由は以下の通りです。
転職して給料上がって時間も増えてレベルの高い人達と一緒に仕事できて会社にも不満なくてとにかく最高って話転職Advent Calendarに書こうかと思ったけどただの自慢話になりそう
— たくじ31(歳) (@takuji31) 2016年11月30日
はっきりこれといったテーマがない感じなので雑多にメモっぽいこと書きます。
自分の価値観なので、それは違うよってことあるかもしれませんし、行き詰まってる人の参考になるかもしれませんし、他の方の考え方を否定するものではありませんのであしからずご了承ください。
1987年3月1日生まれの29歳、大阪市生まれ大阪市育ちの大阪市民で2010年に4年制の専門学校を1年休んで5年で卒業して、東京のモバイル系のベンチャーに新卒入社。
1年半ほどソシャゲの開発と運用をやって、半年ほどスマートフォンの着メロサイトの開発と運用をやっていた。
同時に入社してすぐの頃からAndroidアプリの開発も並行してやっていて、その年の夏に(仕事では)自分にとっても自社にとっても初めてのAndroidアプリをリリースした。
ちょうど2年経った頃、元同僚の誘いで某アパレル系大企業の子会社に入社、日本でトップ3以内にいる某ファッションECサイトのスマートフォンアプリを作ったりなどしていた、iOS開発をやり始めたのはこのタイミング。
色々あって1年ほどで大阪の実家に帰ることになって転職、前職に入社。スマートフォンアプリの受託開発を経験。
半年で辞めようと思ったけどそろそろ5年くらいはどこか1社で頑張った経歴が欲しいと思いつつも2年半ほどで身体の方にガタがきて転職活動を始めて、以前からとある方にお誘いいただいていた縁もあって、今年の1月からはてなに入社した。
振り返ってみるとポジティブともネガティブな理由ともとれそうな感じの転職の仕方をしてきていて、実際そうだと思う。
会社への不満とか自分がここにいて本当に成長できるのかとか、目指しているものが違うとかそういう思いがありつつ、一方でステップアップしたいみたいなことは常に思っているし、すごい人達と仕事したいからそういう人達いるところを探したいみたいなのもある。
はてなに入ってみて11ヶ月経って思うけど、やっぱりすごい人達の集まりって感じだし、YAPCとかで登壇してて何となく雲の上とは言わなくても自分とは全然違う世界にいそうだと思っていた人達と今仕事をしているのがとても良い。
※たぶんフィクションです
実際はてなに入った時も「通勤時間長くて朝つらい(京阪経由で1時間40分、JRでも1時間20分ほど)」問題があったが、今は京都に引っ越して解決している。
この辺は独身だと解決しやすい問題という気がしているが、共働きとか子持ちだと大変そう。
全部自分の話
自分の話かもしれないし、想像かもしれない
多くの人が転職の理由の一つに加える要素の一つだと思う。
一応自分は2回目の転職以外は基本的に上がってるし、残業代やボーナス入った実績値なら一度も下がってないんだけど、オファー時の提示額が下がるの結構モチベに影響ある気がしている。
少なくとも同じような職種に転職する時は上がっておきたい。
準備とかちゃんとして転職成功しようぜ!
こんばんは、最近Androidエンジニアを名乗っていいのか悩ましいid:takuji31です。
今日はRecyclerViewのItemDecorationについて簡単な使い方をまとめてみました。
なお、この記事の一部は本日開催の関西モバイルアプリ研究会#20で発表された内容です。
RecyclerViewの各アイテムに対して何らかの装飾を施す仕組みです。
代表的なのは罫線を引いてくれる(Support Library25.0.0でようやく追加された)DividerItemDecorationやアイテムのDnDやスワイプが行えるようになるめっちゃ便利なItemTouchHelperがあります(というより公式で提供されている子クラスはこの2つだけ)。
色んなRecyclerViewのアイテムに対して簡単な装飾を施したいのなら、レイアウトで頑張るよりこちらでやる方がよいかと思います。
getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state)
指定したViewが本来レイアウトした位置からどれくらいズレるのかを指定するメソッドです。
最後の state
がないメソッドがありますが、これは非推奨です。
view
が対象のView、 outRect
がアイテムのオフセットを返すための Rect
です、 outRect
の left|top|right|bottom
に直接値をセットします。
onDraw(Canvas c, RecyclerView parent, RecyclerView.State state)
装飾の描画時に呼ばれるメソッドです。呼ばれるタイミングは各アイテムの内容が描画される前です。
最後の state
がないメソッドがありますが、これは非推奨です。
c
はRecyclerView全体のCanvasです、各要素に対して呼ばれるのではなく、描画ごとに1回だけ呼ばれます。
このメソッドで装飾を描画することになります。
onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state)
基本的には onDraw
と同じですが、こちらは呼び出されるタイミングがアイテムの内容が描画された後です。
最後の state
がないメソッドがありますが、これは非推奨です。
アイテムに重ねて描画したい場合に使うとよさそうです。
基本的には ItemDecoration
を継承したクラスを作って、getItemOffset
と onDraw
もしくは onDrawOver
(あるいは両方)を実装してやるとよいです。
使う時はRecyclerView.addItemDecoration(RecyclerView.ItemDecoration decor)
を呼んで装飾を追加します。
今回はiOSの UITableViewCell
にある accessoryType
のようなものを作ってみました。
雑にenumを定義しました、とりあえず2タイプだけサポート。
enum class AccessoryType(@DrawableRes val resId: Int) { NONE(0), DISCLOSURE_INDICATOR(R.drawable.ic_chevron_right_black_24dp), CHECK_MARK(R.drawable.ic_done_black_24dp), }
文字列と AccessoryType
を持つ Pair
のリストを持つだけの Adapter
を作りました。
ポイントは ViewHolder.itemView
にsetTag
で AccessoryType
を渡していること。
View
と RecyclerView
から ViewHolder
を取り出すことはできますが、これだと ViewHolder
の型に ItemDecoration
が依存してしまうので、Tagで渡しています。
class Adapter(var items: List<Pair<String, AccessoryType>>) : RecyclerView.Adapter<ViewHolder>() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val inflator = LayoutInflater.from(parent.context) return ViewHolder(view = inflator.inflate(R.layout.recyler_row_simple_textview, parent, false)) } override fun getItemCount(): Int { return items.size } override fun onBindViewHolder(holder: ViewHolder, position: Int) { val (text, accessoryType) = items[position] holder.textView.text = text holder.textView.setOnClickListener { Log.d(javaClass.simpleName, "clicked") } holder.itemView.setTag(R.id.tagAccessoryType, accessoryType) } }
今回は AccessoryType.NONE
以外の時に右側にいい感じに装飾を表示することにしました。
ItemDecoration
全体のコードは↓の通り
class AccessoryItemDecoration(private val context: Context) : RecyclerView.ItemDecoration() { val horizontalMargin by lazy { context.resources.getDimensionPixelOffset(R.dimen.accessory_horizontal_margin) } override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) { val accessoryType = view.getTag(R.id.tagAccessoryType) as? AccessoryType if (accessoryType != null) { if (accessoryType != AccessoryType.NONE) { val drawable = ContextCompat.getDrawable(view.context, accessoryType.resId) outRect.right = drawable.intrinsicWidth + horizontalMargin * 2 } } } override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { val childCount = parent.childCount for (i in 0..childCount - 1) { val child = parent.getChildAt(i) val accessoryType = child.getTag(R.id.tagAccessoryType) as? AccessoryType if (accessoryType != null && accessoryType != AccessoryType.NONE) { val drawable = ContextCompat.getDrawable(child.context, accessoryType.resId) val bounds = Rect() parent.getDecoratedBoundsWithMargins(child, bounds) val left = bounds.right - horizontalMargin - drawable.intrinsicWidth val right = left + drawable.intrinsicWidth val top = bounds.top + ((bounds.bottom - bounds.top - drawable.intrinsicHeight) / 2) + Math.round(ViewCompat.getTranslationY(child)) val bottom = top + drawable.intrinsicHeight drawable.setBounds(left, top, right, bottom) drawable.draw(c) } } } }
getItemOffsets
ViewHolder
でセットした AccessoryType
のTagを取得して、修飾に使うDrawableとmargin分だけ右側にoffsetを設定しています。
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) { val accessoryType = view.getTag(R.id.tagAccessoryType) as? AccessoryType if (accessoryType != null) { if (accessoryType != AccessoryType.NONE) { val drawable = ContextCompat.getDrawable(view.context, accessoryType.resId) outRect.right = drawable.intrinsicWidth + horizontalMargin * 2 } } }
onDrawOver
子の View
を1つずつ取得して、それぞれ必要に応じて Drawable
を描画しています。
parent.getDecoratedBoundsWithMargins(child, bounds)
でそのViewの装飾込みの境界を表すRectが取得できます。
ViewCompat.getTranslationY(child)
でViewのtranslationYが取得できるので、この分描画位置をずらす必要があります。
あとは Drawable
を Canvas
に描画するだけです。
override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { val childCount = parent.childCount for (i in 0..childCount - 1) { val child = parent.getChildAt(i) val accessoryType = child.getTag(R.id.tagAccessoryType) as? AccessoryType if (accessoryType != null && accessoryType != AccessoryType.NONE) { val drawable = ContextCompat.getDrawable(child.context, accessoryType.resId) val bounds = Rect() parent.getDecoratedBoundsWithMargins(child, bounds) val left = bounds.right - horizontalMargin - drawable.intrinsicWidth val right = left + drawable.intrinsicWidth val top = bounds.top + ((bounds.bottom - bounds.top - drawable.intrinsicHeight) / 2) + Math.round(ViewCompat.getTranslationY(child)) val bottom = top + drawable.intrinsicHeight drawable.setBounds(left, top, right, bottom) drawable.draw(c) } } }
今回は分かりやすい例を出すためにこんな感じにしましたが、こういうのはViewに直接レイアウトした方がいいかもしれません。
getItemOffsets
でoffsetを決めるonDraw
か onDrawOver
で描画するRecyclerView.addItemDecoration()
して使う今回のサンプルコードはこちら
こんにちは、Kotlinエバンジェリストになりたいid:takuji31です。
先日EAPでM3が公開されたKotlin1.1の新機能について、あまりちゃんと把握していなかったのでM1から見直してみました。
なお、この内容の一部は Kansai.kt #2で発表されました。
各タイトルは公式ブログから
他の言語にある async/await
とか generator/yield
ですね。
非同期や反復処理をモダンな感じに書けるようになります。
Kotlin 1.1最大の新機能とも言えます。
coroutineは今はKotlin本体と分離されていて、別の依存を追加することで使えます。
compile 'org.jetbrains.kotlinx:kotlinx-coroutines-generate:0.1-alpha-2' compile 'org.jetbrains.kotlinx:kotlinx-coroutines-async:0.1-alpha-2' compile 'org.jetbrains.kotlinx:kotlinx-coroutines-rx:0.1-alpha-2'
それぞれ coroutines-async
が async/await
、 coroutines-generate
が generator/yield
、 coroutines-rx
が RxJavaのObservableをawaitできるライブラリーです。
async/await
は誤解を恐れない書き方をすると、非同期な処理を実行したいメソッド内で async
関数を呼び、その中の非同期にやりたい処理をawait()
で囲ってやります。 Observable
の場合は async/await
ではなく asyncRx/await(First|Last|Single)
を使います。
await
の引数は CompletableFuture
なのでAndroidだと7.0以降(API24)、Javaだと8以降でしか使えません、残念。
めっちゃ雑なサンプル
fun asyncOperation(i: Int): CompletableFuture<Int> = CompletableFuture.supplyAsync { Thread.sleep(1000) i } val future = async<Int> { val i = await(asyncOperation(1)) i } Log.d("MainActivity", "i:" + future.get())
雑に使ってみた感想だと、CompletableFutureをいい感じに使う機能でFutureあまり使わない勢からするとさほど利点はなさそうというイメージ、他言語のものほどはまだ洗練されてなさそうでした、まぁalpha2だしこんなもんでしょう。
型に別名を付ける機能です。 今まで(String, Int) -> Unit
みたいな感じで書いていて面倒なのでわざわざinterface作っていたりしたコールバックとかをいい感じに名前つけられて便利。
typealias VeryUsefulAction = (String, Int) -> Unit
個人的に1.1の今のところ出ている新機能で一番ありがたいです。
関数とかメソッド内で使えるプロパティーらしい、関数内での初期化を簡単に書けたりするようになりそう?
いまいち使いどころがよく分からなかった。
Kotlinでスクリプト書けるようになったらしいです、GradleがKotlinサポートを表明したのは記憶に新しいですが、これもその機能の1つのようです。
Genericsを使っていい感じにEnumの値にアクセスできたりするらしいです、型引数と組み合わせると便利な処理が書けるようになったりしそう。
inline
なのでコンパイル時にインライン展開してくれます。
inline fun <reified T : Enum<T>> enumValues(): Array<T> inline fun <reified T : Enum<T>> enumValueOf(name: String): T
いわゆるmethod referenceみたいなやつですね、今までは Class.method
をサポートしていたけど、新たに variable.method
もサポートするとのことです。
合成値(data classとかPairみたいにvariable.component1()...componentN()で分解できるやつ)の分解を、ラムダ式でもいい感じにやってくれるようになりました。
listOf(Pair(1, 2), Pair(2,3)).map{ (a, b) -> a + b // 3, 5 };
今までは面倒だけど変数定義し直していたりしたので、簡単に書けるようになって便利ですね。
使わない戻り値を_で無視することのできる機能
ラムダ式の引数を無視したり、合成値の分解をした時に途中の値だけ捨てるのに便利ですね。
data class User(val id: Long, val name: String) // ... val (_, name) = user;
数値リテラルの途中に_を挟めるようになりました。10000000 -> 10_000_000
みたいな感じ。
Perlで見たことあるやつって感じで桁の多い数値を表現する時に便利ですね。
sealed classやdata classの定義が柔軟になったとのこと。
data class は非data classを継承できるようになっています。 もちろんopenである必要があるし、プロパティーもopenじゃないと継承できません。
open class User(open val id: Long, open val name: String) data class AdminUser(override val id:Long, override val name: String, val role: String) : User(id = id, name = name)
これは結構ありがたい。
sealed classは今までは必ず子クラスは親の中で定義する必要があった(sealed A を継承する場合は A.B)のですが、これが不要になり、同じファイル内ならどこに書いても良いことになりました。
sealed class Score(val number: Int) class Strike() : Score(10) class Gutter() : Score(0) class Miss() : Score(0)
importする時に若干面倒だったのでありがたいですね。
Kotlin1.1からJava7/8がサポートされました。
ちゃんとJavaの新しいクラスなんかもKotlinで使えます。
JSサポートが強化されているらしいですが、使ってるのを聞いたことがないのでよく分かりませんでした。
結構色々便利にはなっていますが、どれもまだEAPなので変わる可能性はありますし、今後もっと増えるかもしれません。
余談ですが、IDEAの2016.3で1.1M3のプラグインがまともに動かなかったので、Android Studioを使ってAndroidアプリで試しました。
メンターのシニアエンジニアと話していたら、それいいじゃんって感じになったのでブログにまとめてみる。
関西モバイルアプリ研究会でほぼ毎月(といいつつ最近発表回数減ってるけど)Androidネタで発表しているが、最近はそれと同時にブログ記事も公開している。
これらの記事は資料作成と同時に書いていて、LTの開始のタイミングで公開する。
毎月発表していると、資料作成に使える時間は大きなカンファレンスに参加する時より圧倒的に少ない。関西モバイルアプリ研究会はLT形式なので、もちろん資料のボリュームは小さいんだけど、実際に話すネタとして考える内容は20分や50分のトークとさほど変わらないと思う。
人によってはアウトラインをまず書いてから細かく作っていく、という風に作っていくんだろうけど、自分は割とそういうことが得意でないことが多いので、スライドツールを目の前に頭を抱えることが多い。
そこで、先にブログ記事を書いて、それをLTの時間で話せる内容に落とし込んで資料にしている。
はてなブログにはMarkdown記法で書く便利な設定がある。
そして、DecksetというMarkdown記法でスライドツールが書ける便利なアプリがMac App Storeにある、有料。
記事の内容をコピペしつつ、スライドとしての体裁を整える。
ブログ記事の順番に話が並ぶので、発表として破綻しているような順番になったりすることはまずない。
資料に書かない部分は発表ノートにしたりもしている。
書いているといっても実際やってみたのはまだ2回だけなので、もっと改善の余地はありそう。
ただ、最近やっている「思いついたらとにかくブログに書く」と組み合わせることでアウトプット量は圧倒的に増えたという気がしているので、続けていきたい。
うまくいったらそのうち通常のトーク資料作る時にも使っていきたい。