Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
JavaプログラムをGoに移植するためのテクニック――継承と例外
Search
MakKi
July 13, 2019
Programming
2.1k
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
JavaプログラムをGoに移植するためのテクニック――継承と例外
Go Conference'19 Summer in Fukuoka
MakKi
July 13, 2019
More Decks by MakKi
See All by MakKi
テストだけじゃない!インプロセスDBで生まれるGoらしさ
makki_d
0
58
テストだけじゃない!インプロセスDBで生まれるGoらしさ
makki_d
0
74
SQLだけでマイグレーションしたい!
makki_d
0
1.4k
Recap: An Operating System in Go
makki_d
2
200
XSLTで作るBrainfuck処理系
makki_d
0
440
眼鏡と視力についての誤解を解く
makki_d
0
240
標準ライブラリの動向とイテレータのパフォーマンス
makki_d
3
790
range over funcのエラー処理
makki_d
1
1.9k
GoとテストとインプロセスDB
makki_d
3
710
Other Decks in Programming
See All in Programming
Java 27新機能 / Java 27 new features
kishida
2
130
【高い買い物LT会】初任給で話題の国産フィジカルAIを買った話
akagami
PRO
0
160
モジュールの視点からSwiftを読み解く #iosdc
s_shimotori
0
170
GraphRAGのKnowledge Graphを 直接!見る/View-GraphRAG's-KnowledgeGraph-directly!
tyumugi1113
1
310
フロントエンドUIフレームワークのこれまでとこれから
ssssota
5
2.7k
選挙速報を多くのユーザーへ 届ける Live Activities 設計
hamayokokuririn
0
140
Omarchy Tokyo やると聞いて UMPC 買ってセットアップしてきた
mtsmfm
0
140
How I Stole PSI from Android Studio - DroidKaigi2026
worker8
0
120
Streamlitで実現する自然言語データアプリ開発
ayumu_yamaguchi
0
270
Jetpack Compose メカニズム
skydoves
0
410
Starting & Sustaining Code-Based E2E Testing for Non-Coding QA Teams( #jasstniigata )
teyamagu
PRO
1
150
FreeBSDでZabbixを動かす.pdf
kenkino
0
260
Featured
See All Featured
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
360
30k
The Mindset for Success: Future Career Progression
greggifford
PRO
0
490
Exploring anti-patterns in Rails
aemeredith
4
510
My Coaching Mixtape
mlcsv
0
310
A brief & incomplete history of UX Design for the World Wide Web: 1989–2019
jct
2
510
Marketing Yourself as an Engineer | Alaka | Gurzu
gurzu
0
300
Context Engineering - Making Every Token Count
addyosmani
9
1.1k
A Guide to Academic Writing Using Generative AI - A Workshop
ks91
PRO
1
460
Product Roadmaps are Hard
iamctodd
55
13k
The Illustrated Guide to Node.js - THAT Conference 2024
reverentgeek
1
480
Ecommerce SEO: The Keys for Success Now & Beyond - #SERPConf2024
aleyda
1
2.1k
Mind Mapping
helmedeiros
1
360
Transcript
Javaプログラムを Goに移植するためのテクニック ――継承と例外 Go Conference'19 Summer in Fukuoka 牧内大輔 @makki_d
自己紹介 • 牧内大輔 • MakKi / @makki_d • 所属:KLab株式会社 ◦
エンジニアリングマネージャー ◦ オンライン対戦の中継サーバを Goで書いたり ◦ PHP/Python/C#その他必要とあればなんでも • gozxing ◦ https://github.com/makiuchi-d/gozxing ◦ 1D/2Dバーコードリーダーライブラリ ◦ JavaのZXingをGoに移植
会社紹介 KLab(くらぶ)株式会社 • 今日は休日出勤扱い(旅費・宿泊費も会社もち) • モバイルオンラインゲームの開発運用 • 福岡オフィスあります(KBCビル) • KLab福岡Meetup
Twitter:@KLab_KFM Go言語の活用事例 • オンライン対戦同期サーバ • MMORPGのゲームサーバ • インフラ管理系ツール • その他細かいツール類やslackbotなど
なぜJavaをGoに移植するのか
ソフトウェア資産を活かしたい • Goは比較的新しい言語 ◦ 2009年11月10日に発表 • JavaやC++などのソフトウェア資産をGoでも利用したい ◦ Goの標準ライブラリはかなり充実している ◦
その範疇外のライブラリも世の中にはたくさんある • PureなGoで書かれている価値 ◦ クロスプラットフォーム対応のしやすさなど • Go向けに設計し直すのは大変 ◦ 言語構造、思想の違いが大きい ◦ できることなら設計や構造をそのまま利用したい
今日のお話 • Java製1D/2Dバーコードライブラリ「ZXing」をGoに移植しました ◦ https://github.com/makiuchi-d/gozxing • 元のJavaコードとGoのコードを見比べながら 実際に使っているテクニックをいくつか紹介します ◦ クラスと継承
▪ メソッドオーバーライド ◦ 例外 ▪ 例外型の階層構造 ▪ 例外種別判定
諸注意 この発表は、Javaの構造をそのままGoに移植するためのテクニックの紹介です。 元コードの構造をそのまま残すため、Goでは推奨されない書き方が出てきます。 コード断片を表示しますが、見にくかったらごめんなさい。 Javaを題材としていますが、C++やC#のように同様のクラスシステムと 例外機構を持つ言語にも適用できると思います。 もっと良い書き方があれば教えてください。
クラスと継承
Goに移植するときのポイント • 構造体とレシーバ関数でクラスを表現 • 構造体埋め込みで継承っぽい動作 • Goではメソッドオーバーライドができない
メソッドオーバーライドできない • レシーバの型は元の型のまま ◦ 埋め込まれた型のレシーバが呼ばれる ◦ 親クラスからは自身のメソッドになる BarkTwiceの中のdogは*Dogなので *DogのBarkが呼ばれる C++で言う仮想関数テーブルが必要
type Dog struct {} func (dog *Dog) Bark() { fmt.Print("ワン") } func (dog *Dog) BarkTwice() { dog.Bark() dog.Bark() } type AmericanDog struct { *Dog } func (dog *AmericanDog) Bark() { fmt.Print("Bow"); } dog := &AmericanDog{&Dog{}} dog.BarkTwice()
Java: OneDReader, Code128Readerの実装 public abstract class OneDReader implements Reader {
@Override public Result decode(BinaryBitmap image, 略) throws NotFoundException, FormatException { ... Result result = decodeRow(rowNumber, row, hints); ... } public abstract Result decodeRow(int rowNumber, BitArray row, Map<DecodeHintType,?> hints) throws NotFoundException, ChecksumException, FormatException; } public final class Code128Reader extends OneDReader { @Override public Result decodeRow(int rowNumber, BitArray row, Map<DecodeHintType,?> hints) throws NotFoundException, ChecksumException, FormatException { ... } }
Java: OneDReader, Code128Readerの実装 public abstract class OneDReader implements Reader {
@Override public Result decode(BinaryBitmap image, 略) throws NotFoundException, FormatException { ... Result result = decodeRow(rowNumber, row, hints); ... } public abstract Result decodeRow(int rowNumber, BitArray row, Map<DecodeHintType,?> hints) throws NotFoundException, ChecksumException, FormatException; } public final class Code128Reader extends OneDReader { @Override public Result decodeRow(int rowNumber, BitArray row, Map<DecodeHintType,?> hints) throws NotFoundException, ChecksumException, FormatException { ... } } Readerインターフェイスの実装 派生クラスのdecodeRow()呼び出し 派生クラスでdeocdeRowの実装を要求 派生クラスでのdecodeRowの実装 派生クラス
Go: OneDReader, Code128Readerの実装 type RowDecoder interface { DecodeRow(rowNumber int, row
*gozxing.BitArray, 略) (*gozxing.Result, error) } type OneDReader struct { RowDecoder } func NewOneDReader(rowDecoder RowDecoder) *OneDReader { return &OneDReader{rowDecoder} } func (this *OneDReader) Decode(image *gozxing.BinaryBitmap, 略) (*gozxing.Result, error) { ... result, e := this.DecodeRow(rowNumber, row, hints) ... }
Go: OneDReader, Code128Readerの実装 type RowDecoder interface { DecodeRow(rowNumber int, row
*gozxing.BitArray, 略) (*gozxing.Result, error) } type OneDReader struct { RowDecoder } func NewOneDReader(rowDecoder RowDecoder) *OneDReader { return &OneDReader{rowDecoder} } func (this *OneDReader) Decode(image *gozxing.BinaryBitmap, 略) (*gozxing.Result, error) { ... result, e := this.DecodeRow(rowNumber, row, hints) ... } Readerインターフェイスの実装 埋め込んだRowDecoderのDecodeRowの呼び出し オーバーライドするメソッドを集めた インターフェイス(仮想関数テーブル) 仮想関数テーブルの埋め込み 初期化関数で実装を要求
Go: OneDReader, Code128Readerの実装 type RowDecoder interface { DecodeRow(rowNumber int, row
*gozxing.BitArray, 略) (*gozxing.Result, error) } type OneDReader struct { RowDecoder } func NewOneDReader(rowDecoder RowDecoder) *OneDReader { return &OneDReader{rowDecoder} } func (this *OneDReader) Decode(image *gozxing.BinaryBitmap, 略) (*gozxing.Result, error) { ... result, e := this.DecodeRow(rowNumber, row, hints) ... } Readerインターフェイスの実装 埋め込んだRowDecoderのDecodeRowの呼び出し オーバーライドするメソッドを集めた インターフェイス(仮想関数テーブル) 仮想関数テーブルの埋め込み 初期化関数で実装を要求 type code128Reader struct { *OneDReader } func NewCode128Reader() gozxing.Reader { this := &code128Reader{} this.OneDReader = NewOneDReader(this) return this } func (*code128Reader) DecodeRow(略) (*gozxing.Result, error) { ... }
Go: OneDReader, Code128Readerの実装 type RowDecoder interface { DecodeRow(rowNumber int, row
*gozxing.BitArray, 略) (*gozxing.Result, error) } type OneDReader struct { RowDecoder } func NewOneDReader(rowDecoder RowDecoder) *OneDReader { return &OneDReader{rowDecoder} } func (this *OneDReader) Decode(image *gozxing.BinaryBitmap, 略) (*gozxing.Result, error) { ... result, e := this.DecodeRow(rowNumber, row, hints) ... } Readerインターフェイスの実装 埋め込んだRowDecoderのDecodeRowの呼び出し オーバーライドするメソッドを集めた インターフェイス(仮想関数テーブル) 仮想関数テーブルの埋め込み 初期化関数で実装を要求 type code128Reader struct { *OneDReader } func NewCode128Reader() gozxing.Reader { this := &code128Reader{} this.OneDReader = NewOneDReader(this) return this } func (*code128Reader) DecodeRow(略) (*gozxing.Result, error) { ... } 継承元を埋め込み 仮想関数テーブルとして自身を渡す 派生クラスのDecodeRow実装 派生クラス
メソッドオーバーライドのテクニック • 継承元構造体 ◦ 仮想関数テーブルとなるインターフェイスを持つ ▪ 派生構造体のメソッドを呼び出せる ◦ 初期化関数で実装を要求できる •
派生構造体 ◦ 継承元構造体を埋め込む ▪ 継承元のフィールドやメソッドを利用できる ◦ 継承元構造体の初期化関数に自身を渡す ▪ メソッドオーバーライド
例外
JavaからGoへの書き換え try { alignmentPattern = findAlignmentInRegion(moduleSize, estAlignmentX, estAlignmentY, i); break;
} catch (NotFoundException e) { // try next round } alignmentPattern, e = this.findAlignmentInRegion(moduleSize, estAlignmentX, estAlignmentY, i) if e == nil { break } if _, ok := e.(gozxing.NotFoundException); !ok { return nil, e }
JavaからGoへの書き換え try { alignmentPattern = findAlignmentInRegion(moduleSize, estAlignmentX, estAlignmentY, i); break;
} catch (NotFoundException e) { // try next round } alignmentPattern, e = this.findAlignmentInRegion(moduleSize, estAlignmentX, estAlignmentY, i) if e == nil { break } if _, ok := e.(gozxing.NotFoundException); !ok { return nil, e } NotFoundExceptionだけ握りつぶす(それ以外は送出) NotFoundException以外を送出 例外非発生でbreak (for文の中) nilチェックしてOKならbreak 戻り値にerrorを追加
JavaからGoへの書き換え try { ... } catch (FormatException | ChecksumException e)
{ // 特定の例外種別専用の処理 ... } ... switch e.(type) { case nil: // break case gozxing.FormatException, gozxing.ChecksumException: // 特定の例外種別専用の処理 default: return nil, e }
JavaからGoへの書き換え try { ... } catch (FormatException | ChecksumException e)
{ // 特定の例外種別専用の処理 ... } ... switch e.(type) { case nil: // break case gozxing.FormatException, gozxing.ChecksumException: // 特定の例外種別専用の処理 default: return nil, e } 2種類の例外だけ捕捉して特別処理 2種類の例外だけ捕捉して特別処理 他の例外は明示的に送出 例外非発生なら明示的になにもしない
Goに移植するときのポイント • 例外はthrowではなく戻り値として送出する ◦ error型 ◦ 例外を投げうるメソッドにはすべて戻り値を足す ◦ 呼び出し側でnilチェック •
型による例外種別判定 ◦ 型アサーション、タイプスイッチ ◦ 元のJavaの設計そのまま • 例外の階層的なグルーピング ◦ 継承による型の階層関係
interface継承による例外型の階層関係の模倣 • Javaでの継承関係と同じにする ◦ errorを継承してerrorとしても扱う • 実体はerrorを埋め込んだ構造体 type Exception interface
{ error exception() } type ReaderException interface { Exception readerException() } type NotFoundException interface { ReaderException notFoundException() } type notFoundException struct { error } func (_ notFoundException) exception() {} func (_ notFoundException) readerException() {} func (_ notFoundException) notFoundException(){} Exception ReaderException WriterException NotFoundException FormatException
型による例外種別判定 • 型アサーションによる判定 • 型スイッチによる判定 ◦ 複数catchがある場合などに便利 欠点 • ラップされると型が変わる
◦ xerrorsのErrorfの ": %w" ◦ 型で判定できなくなる func DoSomething() error { return notFoundException{ xerrors.New("エラー"), } } ... err := DoSomething() if err != nil { if _, ok := err.(ReaderException); ok { // 何か処理 } } switch err.(type) { case nil: // break case NotFoundException: // 何か処理 }
xerrors.As関数による判定も可能 • xerrors.As関数の実装 ◦ 次の条件に合うまで Unwrap() ▪ targetの型に代入可能 ▪ err.As(target)
がtrue • 代入可能かを利用した判定 ◦ targetにはinterface型も渡せる ◦ interface継承の例外型で使える • Asメソッドによる判定 ◦ 今回は省略 ◦ 例外の型ごとに柔軟に設定できる func DoSomething() error { return notFoundException{ errors.New("エラー"), } } func DoSomething2() error { err := DoSomething() return xerrors.Errorf("エラー2: %w", err) } ... err := DoSomething2() if err != nil { var exception ReaderException if xerrors.As(err, &exception) { // 何か処理 } }
まとめ
今日紹介したテクニック • クラスと継承 ◦ メソッドオーバーライドの模倣 ▪ 仮想関数テーブルインターフェイス • 例外機構 ◦
例外型の階層関係の模倣 ▪ インターフェイス継承 ◦ 型による例外種別判定 ▪ 型アサーションやタイプスイッチ ▪ xerrors.As関数
おしまい 質問の他、もっと良い書き方の提案などあればお願いします。