サナギわさわさ.json

サナギさんとキルミーベイベーとプログラミングが好きです

Java→Kotlin変換時のハマりポイント (potatotips #53)

potatotips#53Androidブログまとめ枠で参加させていただきました。 初参加でしたがどの発表もクオリティが高く楽しかったです。次に参加する時は自分もLTしたい。

全発表を議事録的にまとめようかと思っていたのですが、当日の夜にはすでにまとめられていました。すごい。

kumamotone.hatenadiary.jp

というわけで、今回はLTの中で一番心に残った@paraya3636さんの発表「J2Kコンバータをカスタマイズする」について記事を書こうと思います。

発表資料はこちら

J2Kコンバータをカスタマイズする ver: 5min - Speaker Deck

Java → Kotlinの流れ

GoogleAndroid開発のためのファーストクラス言語としてKotlinを公式サポートすることを発表したのは2017年の5月でして、そのあたりからKotlinを本番プロダクトに導入する流れが加速したように思います。

jp.techcrunch.com

KotlinとJavaは共存できますので、新しく追加するコードはKotlinで書く、みたいな部分的な導入をしている方もいると思います。しかし既存のJavaコードをKotlinに書き換えKotlinメインにシフトするという選択をした方も多いのではないでしょうか。(弊社は後者です)

ここで大量のJavaコードをKotlinに変換するという結構重めのタスクが出てきます。

Kotlinコンバートの方法と辛さ

発表でも触れられているように、通常JavaからKotlinに変換する際はAndroid Studio標準搭載のJ2Kコンバータを利用します。

このコンバータは結構良くできていて、多くのコードがエラーなく変換できます。個人的にはObjective-CをSwiftに変換した時よりも数倍楽でした。

しかしいくつか問題はあり、その一つがNullableをNonNullに変換してしまうという点です。

具体的には下記のように、引数にNullableアノテーションがない場合にNullableがNonNullに変換されてしまいます。(発表スライドよりコードを引用させていただきます)

public interface SampleInteface {
    //Nullable引数
    String getText(Context context);
    
    int getValue(Context context); 
}
interface SampleInteface {
    //NonNull引数
    fun getText(context Context): String
    
    fun getValue(context Context): Int 
}

この変換をされたKotlinコードをJavaからnull引数で呼び出すと実行時に以下の例外でクラッシュしてしまいます。

IllegalArgumentException: Parameter specified as non-null is null

この例外はKotlinとJavaを共存させていると結構見るやつで、折角KotlinがOptionalで型安全を担保しているのに勿体無いなといつも思います。

所感ですが、これはKotlinとJavaの連携がスムーズすぎて呼び出し時に特に意識しなくても呼び出せてしまうのが一因な気がします。 SwiftとObjective-Cは連携自体が少し面倒なので、少なくとも自分の周りではあまりこういう事は起こっていません。

J2Kコンバータのカスタマイズ

この問題に対する解決策として、J2Kコンバータ自体をカスタマイズしてNullableを保持したまま変換する、という方法が発表では触れられています。

正直自分はこの方法は発想から抜けており、目視でのチェックとアプリを手元で動かしてのチェックを頑張るというぬくもり溢れる対応で解決していました。お恥ずかしい限りです。

自分でもJ2Kコンバータのカスタマイズをして動かすところまで記事にしようと思ったのですが、少し重かったのでそこまで行けませんでした。すみません。。。

自分がJava→Kotlinの変換でハマったところ

代わりと言っては何ですが、自分がJava→Kotlinの変換でハマったところについて少し触れておきます。もし参考になれば幸いです。環境はAndroidStudio2.3 + Kotlin 1.1.51です。

ライフサイクル系関数の実行時例外

onActivityResultをJ2Kコンバータで変換すると以下のようになります。

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
}

しかしonActivityResultのIntentにはシステムからnullが渡される可能性がありますので、 この変換されたコードは実行時例外になります。なので手動で以下のように書き換える必要があります。

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
}

他にもonCreate, onCreateView, onViewCreated, onAttachなども変換ミスが起こることがあります。これに関しては@Overrideアノテーションがある時は変換が上手く行ったりと、イマイチ変換に成功する条件が分かっていません。 取り急ぎ正しい変換後の形を記述しておきます。もし何か間違いあれば教えてください。

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
}

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                          savedInstanceState: Bundle?): View? {
    return inflater.inflate(R.layout.fragment_base, container, false)
}

override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
}

override fun onAttach(context: Context?) {
    super.onAttach(context)
}

Listener系の変換時エラー

Animation.AnimationListenerのような関数に関しても、自分の環境では自動変換が上手く行きませんでした。

//変換前
anim.setAnimationListener( new AnimationListener() {
    @Override public void onAnimationStart(Animation animation) {}
    @Override public void onAnimationRepeat(Animation animation) {}
    @Override public void onAnimationEnd(Animation animation) {}
});
//変換後(コンパイルエラー)
anim.setAnimationListener(object : AnimationListener() {
    fun onAnimationStart(animation: Animation) {}
    fun onAnimationRepeat(animation: Animation) {}
    fun onAnimationEnd(animation: Animation) {}
})

以下のように手動で変換します。

anim.setAnimationListener(object : Animation.AnimationListener {
    override fun onAnimationStart(animation: Animation) {}
    override fun onAnimationRepeat(animation: Animation) {}
    override fun onAnimationEnd(animation: Animation) {}
})

Dagger2の移植が辛い

弊社ではDagger2.11を使用していたのですが、これをKotlin移植する際に色々とハマりました。取り急ぎ、現在問題なく動いているKotlin移植後のコードのみ掲載します。もし何か意見・問題点などあれば是非お願いします。

class SampleApplication : Application(), HasActivityInjector, HasServiceInjector {
    @Inject
    lateinit var activityDispatchingAndroidInjector: DispatchingAndroidInjector<Activity>

    @Inject
    lateinit var serviceDispatchingAndroidInjector: DispatchingAndroidInjector<Service>

    override fun onCreate() {
        super.onCreate()
        DaggerAppComponent.builder().application(this).build().inject(this)
    }

    override fun activityInjector(): DispatchingAndroidInjector<Activity> {
        return activityDispatchingAndroidInjector
    }

    override fun serviceInjector(): AndroidInjector<Service> {
        return serviceDispatchingAndroidInjector
    }
}
class SampleActivity : AppCompatActivity(), HasSupportFragmentInjector {
    
    @Inject
   lateinit var fragmentDispatchingAndroidInjector: DispatchingAndroidInjector<Fragment>
    
   override fun onCreate(savedInstanceState: Bundle?) {
        AndroidInjection.inject(this)
        super.onCreate(savedInstanceState)
   }
   
   override fun supportFragmentInjector(): AndroidInjector<Fragment> {
        return fragmentDispatchingAndroidInjector
   }
}
        
class SampleFragment : Fragment() {
    
    override fun onAttach(context: Context?) {
        super.onAttach(context)
        AndroidSupportInjection.inject(this)
    }
}       

まとめ

Java → Kotlinの変換では色々とハマる点もありますが、J2Kコンバータは基本的に質が高く、割とスムーズに変換できます。 ただNullable周りでやられる事が多々ありますので、注意が必要ですね。

良い勉強会でした。また是非参加したいと思います!

メンタリングと心理的安全性「エンジニアリング組織論への招待 - 第2章 - 」

先日「エンジニアリング組織論への招待」の第1章についての感想を書きましたが、今回は第2章について書きます。

kakakazuma.hatenablog.com

第2章はメンタリングがテーマです。メンタリングという用語自体は最近良く聞くものではありますが、その詳しい手法については恥ずかしながら本書を読むまであまり知らないままでした。

ソフトウェア開発の技術は仕事をやっていく中で自然と覚えていきますし、先輩の仕事から学べることも多い気がします。それに対してメンタリングなどの人の成長に寄与するための技術に関しては、必要性が表面化しにくい上に人を見て学ぶ事が難しいので、自分で明示的に学ぼうとする意思が必要な気がします。

メンタリングとは

  • メンタリングとは、知識のある人がない人に対して上から押し付けるような教育方法ではない。対話を通じてメンタリングする人の思考力を一時的に貸し出し、思考の幅を広げていく事でその人の歪んだ認知を補正し、次の行動を促し成長させていく手法である。

  • メンタリングは、「自ら考える人材を作る」テクニックである。人は与えられたと思っている役割に対して、自分の思考を閉じてしまうという習性がある。与えられた役割の中で自分自身が「心地よく」いられる思考の範囲や行動の範囲を「コンフォートゾーン」と呼び、自分自身のコンフォートゾーンを変えるのは難しい。

コンフォートゾーンから抜け出すのはなかなか難しく、例えば私は「自分はベストを尽くしてプロダクトを作る、企画やマーケは自分の仕事ではない」的な発想に陥りがちです。責任範囲を自分の得意分野だけに絞る発想は非常に心地良いですし、自分ではタスクにフォーカスして効率良く働けていると思いがちなので注意が必要です。

自己効力感と自立型人材

  • 自分から考えて動いた結果、評価されたとか、周囲からの尊敬を集めたとか、そういったポジティブな結果を手に入れた人は、「自立的に動くことは楽しい」という感覚を得ることができ、これを「自己効力感」と呼ぶ。(逆の感覚は「学習性無気力」と呼ぶ)

  • メンタリングは自立型人材を作るために、対話によって思考の範囲を限定する枠を取り外し、自力で問題解決ができるように促していく。そして、その体験を通して自立的な思考を行うことの快感(自己効力感)が、依存的な思考を行う快感(コンフォートゾーン)を上回るようにメンティを導いていく。

  • メンタリングが効果的なものになるためには、以下の関係性が必要である。

    • 謙虚:互いに弱さを見せられる
    • 敬意:互いに敬意を持っている
    • 信頼:互いにメンティの成長期待を持っている
  • メンターは、「何か課題を指摘するための存在」ではなく、課題に一緒に向き合い成長を支援するというコミットが求められる。

  • 個人の成長を階段にたとえて「成長の階段を上る」と表現することがあるが、メンタリングでメンティに階段を上らせるには次のことをする必要がある。

    • 階段を認識させる
    • 壁に梯子をかける
    • 階段を上りたくさせる

個人的には階段を認識させるというフェーズが一番忘れがちでして、メンターとして認識している課題をそのまま解かせてしまう事が多いです。例えばDB設計の分野で成長してほしい時などは、簡単な設計の仕事を振ってみてフィードバックを返すなどすると、本人が自分で課題に気づくので結果的に成長速度が上がるような気もします。

他者説得と自己説得

  • メンタリングがティーチングに比べて優れているポイントは、気がついたことの応用力が身につく点である。この応用力は、他の人から与えられた知識ではなく、自ら獲得した知識だと感じる事によって生まれる。人から与えられた説得による知識を「他者説得」、自分自身で気がついたことを「自己説得」と呼ぶ。

  • 他者説得の特徴

    • 他人が答えを教える
    • 体感を伴わない
    • 理解を確認できない
  • 自己説得の特徴

    • 他人が質問で促す
    • 体感を伴う
    • 行動の変化が発生しやすい
  • 自己説得を生み出すには、適切な質問の積み重ねが重要である。質問によって、より望ましい解決策を自ら発見できるよう促す事ができる。問題解決をするのはメンターではなくメンティであり、当人が抱える問題は当人にしか解決できないので、解決策を言うこと自体が相手への敬意を欠いた行為であると言える。

  • メンタリングを進めるにあたって、メンターはメンティが「悩んでいる」のか「考えている」のかを判断することが重要である。「悩んでいる」状態は手が動いていない状況が続くことで観測でき、この時にはサポートが必要である。

  • メンタリングの基本スキルとして「傾聴」がある。傾聴では、相手を中心としながら相手の思考が整理され、前向きに考えられるよう支援するように意識して会話を行う。

  • 傾聴においては「共感」を伝える事が大事だが、共感という言葉の意味は「相手がそのような気持ちになった理由を理解する」ことである。それに対して「自分が相手と同じ気持ちになる」ことを同感という。傾聴において示すべきは「共感」であって「同感」ではない。

共感という言葉は様々な所で様々な意味で語られている気がしますが、個人的には本書の「相手がそのような気持ちになった理由を理解すること」という定義は割としっくりきました。悩みを聞く上で共感が大事だというのは良く言われる事ですが、自分が相手と同じ気持ちになってしまうと相談者と同じ認知の中に入り込んでしまうので、かえって悩み解決の妨げになる気がします。

心理的安全性とアクノレッジメント

  • 近年、チームの生産性と強い関係性のある要員として「心理的安全性」という用語が注目を浴びているが、これは「対人リスクを取っても問題ないという信念がチームで共有されている状態」というような定義がされている。メンタリングにおいてもメンターとメンティの間で「心理的安全性」は極めて重要である。

  • 心理的安全性はいわゆるぬるま湯な関係性とは違い、「問題点の指摘」「自分の弱みの開示」「失敗の報告」などの対人リスクが高い行動が増えている状態の事を意味する。

  • メンターとメンティの間にも「心理的安全性が高い」状態、すなわち「どんなダメなところを見せても、関係性が破綻することはないという確信」をメンティに抱いてもらう必要がある。このために「メンティ自身の存在を認めている」というメッセージを発し続ける必要があり、これを「アクノレッジメント」と呼ぶ。

  • アクノレッジメントは「承認」を意味し、メンターはメンティの存在に対して、承認しているというメッセージを発し続ける必要がある。これは「褒めること」と同一視されがちだが、「よかった結果を褒める事」とは違う。

  • 相手に対して興味関心を持ち、変化にいち早く気がつき、時間を費やして言葉や行動を通じて伝える事がアクノレッジメントの基本である。アクノレッジメントには3つの段階がある。

    • 存在承認とは、相手が今ここにいてくれてありがたいというメッセージ。挨拶をする、会った時に笑顔である、頑張っている様子を励ますなど。
    • 行動承認とは、「結論から話すようになった」「前よりよく調べてある」などポジティブな行動をとった時にその行動を言葉に出して伝える事。これによってこの行動は承認されているのだと相手が感じる事がある。
    • 結果承認とは、「~はすごい成果だね」のように出来上がったものに対してそれを主観を持って伝える事。
  • 必要なのは結果だけではなく行動、行動だけでなく存在への承認である。承認を示す一番わかりやすいものが言葉をかける事、また感謝を伝える事である。

  • メンターがメンティに相談して意見を求めるというのもアクノレッジメントの一つである。頼られるという体験は、強烈な自己承認と自己効力感を生み出す。

心理的安全性という用語は割と色々な所で使われている気がしますが、いわゆる「ぬるま湯」な状態との違いはあまり語られていない気がします。

また、スタートアップではメンバー間での衝突が多くなりがちだと思いますが、互いに存在承認が発信されていれば破綻はしないような気がします。その点リモートワークだと存在承認を発信するのが難しく、ここは大きな課題の一つです。

成功体験が無い人にどうやって最初の成功体験を積ませるか、というのも良く語られる問題だとは思いますが、存在承認/行動承認から積み重ねていくというのが大事なのかもしれません。

まとめ

第2章はメンタリングの話でした。メンタリングの技術は「コードレビュー」「ペアプロ」「チームマネジメント」などのソフトウェア開発のプロセスでも必要になりますので、習得していきたいと思います。

なお、弊社では現在エンジニアを募集中です。この記事を読んでもし興味がおありでしたら是非オフィスに遊びに来て下さい!

www.wantedly.com

スタートアップでの経験から読み解く「エンジニアリング組織論への招待 - 第1章 - 」

お久しぶりです。前職を辞めて株式会社GlobeeのCTOになってから約10ヶ月が経ちますが、本当に色々な事がありました。 前職でエンジニアをやっていた時に私が解いていた課題は

  • 大規模データ収集基盤を低コストで安定運用するためのアーキテクチャ設計
  • 大規模データ分析を可能にするHadoop基盤構築

など純粋に技術的なものが殆どであり、プライベートでの勉強もそちらに寄っていました。
(↓その時に書いた記事)

qiita.com

しかし現職で解かなければいけない課題は

  • より多くの人に使われるサービスにするためにはどうすれば良いのか
  • 同じ予算でより開発スピードを上げるためにはどうすれば良いのか

など曖昧なものが多く、私個人の技術力だけで解決することが難しいものばかりです。

ですので最近は純粋な技術力というよりも、エンジニアリングの力をビジネスに効果的に反映させるための力が欲しいと感じています。

そんなこんなで先日「エンジニアリング組織論への招待~不確実性に向き合う思考と組織のリファクタリング」を読みましたので、ここ10ヶ月の実体験と絡めて感想を書きます。

blog.shibayu36.org

エンジニアリング = 不確実性の削減

  • エンジニアリングは日本では工学と訳される。理学が物理学や化学のように自然の原理を説明していく学問であるのに対して、工学はそれらに依拠しながら何か役に立つもの実現していく学問である。

  • 何かを実現するというのは、曖昧な要求を明確な何かに変えることを意味する。すなわち、曖昧さを減らし、具体性・明確さを増やす行為がエンジニアリングと言える。

本書のエンジニアリングに対する定義は私がエンジニアリングに対してずっと抱いていたイメージとかなりの部分で一致していました。

ただ私は、エンジニアリングを「技術を用いて」不確実性を削減するものだと矮小化して捉えていました。 エンジニアリングという行為は不確実性の削減が本質であり、技術的な部分は本来そのための手段の一つに過ぎません。

少し具体的な例を挙げます。 私はGlobeeにジョインした当初、「ビジネスサイドが出したアイデアを最高の品質でできるだけ早く世に出す事」が自分の役割であると考えていました。

そのため、開発に割く時間をできるだけ長くする事を重要視し、自分がビジネスサイドに深く関わる事を嫌う傾向にありました。 しかし、スタートアップではアイデア自体の不確実性が非常に高いため、まず削減すべきはアイデア自体の不確実性です。

スタートアップのエンジニアはある程度の時間をビジネスサイドに割く事で、結果的にプロダクトの質も上がるのではないかと思います。

マイクロマネジメント型の組織は弱い

  • 企業においては、社員全員の力を活用するために、誰かに指示をしてその誰かも他の人に指示をするという指示の連鎖が必要になる。そのため、上位に行けば行くほど指示は曖昧になっていく。

  • 具体的で細かい指示を出さないと動けないマイクロマネジメント型の組織では、指示する側の知的能力がそのまま組織の知的能力になってしまうため、抽象化された自由度のある指示でも動ける自己組織化された組織よりも弱い。

弊社の開発体制は今の所マイクロマネジメント型の組織寄りになってしまっており、ここは課題の一つです。 ここに関しては後日また詳しく書こうと思います。

論理的思考の盲点

  • 論理的思考とはすなわち演繹的思考のことであり、前提であるルールと事象から結論を導く思考方式である。

    • ルール:人間は皆死ぬ
    • 事象:私は人間である
    • 結論:私は死ぬ
  • これが成り立つためには、以下の2つの前提が必要である。

    • ルールと事象を正しく認知できること
    • 正しく演繹できること
  • 事実を正しく認知するためには、自分の認知がいつ、どのように歪むのかを知る必要がある。

  • 論理的に考えるためには、自分が非論理的に考えてしまう瞬間を知る事が必要である。論理的思考力と言うのは、「感情的になる瞬間を知り、その影響を少なくできる」能力でもあると言える。

  • 学力テストは通常1人で論理的な思考を用いて行うが、仕事は立場の違う複数人で行うため、コミュニケーションの失敗が論理的思考力を制限してしまう。どんな時に自分は論理的でなくなり、人は論理的でなくなる可能性があるのかという事を知った上で問題解決に臨むのが論理的思考の盲点を知るという考え方である。

スタートアップで働いていると、前職と比べて論理的でなくなる瞬間が多いように思います。

これは

  • 長時間労働による肉体的・精神的疲労
  • 会社のキャッシュが徐々に減っていく事による焦燥感

などの初期のスタートアップ特有の事情によるものが大きいです。後者に関しては割とどうしようも無いのですが、前者に関しては

  • 休む時はしっかり休む
  • 定期的に運動を行う

などである程度対処可能な気がします。

www.slideshare.net

また、自分が過去に感情的になってしまったタイミングを思い返すと以下のような共通点がある事に気付きました。

  • 議論を終えて早く開発を進めたいと思っている時
    • 作業中に声をかけられた時
    • 議論が長引いた時(特に夜)
  • 自分の意見をすぐに否定された時

この条件に当てはまるような時には、感情的になりやすいので注意するようにしています。また自分が感情的になりやすい瞬間については周囲にも共有しており、例えば作業中にイヤホンをしている時はできるだけ声をかけないようにしてもらっています。

経験主義と仮説思考

  • 仕事では、問題を解くのに必要な情報が目の前にないのであればそれを入手するために行動し、問題を明晰化する必要がある。

  • 情報を入手するために行動を起こしてその経過を観察し、そこから問題解決を行う考え方を経験主義と言い、限定された情報から全体像を想定し、それを確かめる事で少ない情報から問題解決に向かう思考様式を仮説思考と言う。

  • 今現在解くことができない難しい問題は、「何が分かれば分かるのか」を考え、それを確かめる事に変換する。

  • 経験主義は、単純に「やってみなければわからない」という論理ではなく、「知識」=「経験」を行動によって手に入れるということである。そのためには、「行動できる事は何か」と「行動の結果起きた事を観察できるか」という2点が重視される。

  • トム・デマルコは「観測できないものは制御できない」という名言を残した。これに習うならば、変化を観測できないものは間接的にすらコントロールできないという事になる。

  • 私たちは何か問題に出くわすと、「コントロールできないもの」を操作して「観測できないもの」を改善するという不可能な問題設定を行ってしまいがちである。「コントロールできるもの」を操作し、「観測できること」を通じてその結果を知識にするしかない。

  • PDCAサイクル「何が仮説なのか」と「それはどのようにしたら推論できるのか」の2つが揃っている必要がある。

スタートアップでは「やってみなければわからないからとりあえずやってみよう」という論理が強くなりがちですが、ここで忘れてはならないのは

  • どういった仮説を検証するためにやるのか
  • 行動の結果、仮説が正しかったのかを統計的に検証できるか

の2つです。この2つが揃っていないPDCAサイクルは回しても実際意味が無かったという事が多いです。最初の方は自分もこれを割とやってしまっていて、しかもPDCAサイクルを上手く回せているつもりだったので最悪でした。たまにまぐれで上手くいってしまうのもタチが悪いです。

これはグロースハックのプロセスにも通ずるものがある気がします。 詳しくは下記をご参照ください。

growiz.us

まとめ

とりあえず第1章を読んで考えた事をまとめました。第1章は個人での仕事にも使えるような内容が多いので、是非色々な人に読んで欲しいなと思います。

第2章はメンタリングの話、第3章以降はチームや組織の作り方の話になっていきますので、これに関しても後日記事にできればと思います。

なお、弊社では現在エンジニアを募集中です。この記事を読んでもし興味がおありでしたら是非オフィスに遊びに来て下さい!

www.wantedly.com