[Scala] scalaのBigDecimalをjava.text.DecimalFormatで変換するとずれることがある

なんかまだ合点がいってないのだが、細かい桁計算が必要でscalaのBigDecimalを使っていて、それを文字列で打ち出す必要が出た際に、javaのDecimalFormatを使ったら出力文字列が合致しなかった。

val a = "4.95"
val b = "0.000000000000000001"

val bigA = BigDecimal(a)
val bigB = BigDecimal(b)

val bigC = bigA+bigB

bigC.toString() // シンプルにtoStringにしたほうが正しい文字列になった

val fm = new java.text.DecimalFormat("0.###################")

println(fm.format(bigA))
println(fm.format(bigB))
println(fm.format(bigC)) // bigA+bigB に文字列一致しない

val fm2 = new java.text.DecimalFormat("0.0000000000000000000")

println(fm2.format(bigA))
println(fm2.format(bigB))
println(fm2.format(bigC)) // bigA+bigB に文字列一致しない

REPL出力

a: String = 4.95
b: String = 0.000000000000000001

biga: scala.math.BigDecimal = 4.95
bigb: scala.math.BigDecimal = 1E-18

bigc: scala.math.BigDecimal = 4.950000000000000001

res0: String = 4.950000000000000001
// シンプルにtoStringにしたほうが正しい文字列になった

fm: java.text.DecimalFormat = java.text.DecimalFormat@674dc

4.95
0.000000000000000001
4.95  // bigA+bigB に文字列一致しない

fm2: java.text.DecimalFormat = java.text.DecimalFormat@674dc

4.9500000000000000000
0.0000000000000000010
4.9500000000000000000  // bigA+bigB に文字列一致しない

ネットで調べて scalaのBigDecimalとjavaのBigDecimalがちょっと違うよ(MathContext.UNLIMITED設定関係)って話は出てきたが、DecimalFormatが違うって話はよくわからんかった。

[Apache poi]そろそろXSSFがデフォルトでよいんじゃないかな

賛否はあるもののお客さんからはExcel等でドキュメントを要求されることが多いことも事実。なのでApache poi(Apacheで出しているMS Excel.Word等MS文書をJavaで操作するライブラリ)などでドキュメント自動生成みたいなところを結局最近やっているのだが、Excel操作ライブラリはXSSF系APIとHSSF系APIに分かれている。

HSSFは拡張子xlsの旧形式、XSSFは拡張子xlsxの今の形式用のAPI。Excelも長いのでAPIが2系統に分かれているのは仕方ないと思うものの、そろそろXSSFがデフォルトでよいんじゃないかなぁ。

Workbook wb = WorkbookFactory.create("file.xslx")
↓
XSSFWorkbook wb = (XSSFWorkbook)WorkbookFactory.create("file.xslx")
または
XSSFWorkbook wb = new XSSFWorkbook("file.xslx")

IDEのコンテキストメニューでAPIがさくっと出ないし、ネットの情報でもHSSFの情報と混じって小一時間悩むし。

[sbt]あまり複雑なことを考えずに機能を追加する

正直まだsbtは理解できていない。まぁ依存性管理ツールはmakeにしろMSbuildにせよ複雑すぎて中をきちんと使いこなすとかできてなかったし。出来合スクリプトで困らんし。

なので専門家からするとまずいのかもしれないが、とりあえず既存のビルドの動きに何かを付け足すくらいの話向け。

  1. ちょこっと作業をまとめたバッチを作りたいだけなのよ→コマンドを使う
    https://www.scala-sbt.org/1.x/docs/ja/Howto-Sequence-using-Commands.html
build.sbt

commands += Command.command("batchtest") { state =>
  "testOnly somepackage.ValidationSpec > list.txt" ::
  "testOnly somepackage.ExecSpec >> list.txt" ::
    state
}
// commandsの使い方はよいがここでリダイレクトは使えないな。。。

2. タスクを作りたいのよ、タスクの前後にラベルを出したいのよ→ 単純なタスク作成
https://qiita.com/mtn81/items/ce482ed16a19f770cc68

3. sbtから外部コマンドを呼びたいのよ→ shell起動
https://www.scala-sbt.org/release/docs/Process.html

lazy val batch = taskKey[Unit]("外部バッチ")
batch in Test := {"powershell.exe ls " !}

4. 依存関係は悩まずにタスクからタスクを呼びたいのよ→ runTask, runInputTask
https://stackoverflow.com/questions/32142055/how-to-call-sbt-inputtask-dynamically

build.sbt

lazy val batch2 = taskKey[Unit]("ドキュメント生成呼び")
batch2 in Test := (doc in Test).value

lazy val batch3 = taskKey[Unit]("テスト1つだけ呼び")
batch3 in Test := (testOnly in Test).toTask(" somepackage.ValidationSpec").value
(↑ toTask(" の部分のスペースが重要)

5. 依存関係は悩まずにタスクでタスクを連続実行させたいのよ→Def.sequential
https://www.scala-sbt.org/1.x/docs/ja/Howto-Sequential-Task.html

build.sbt

lazy val batch4 = taskKey[Unit]("タスク連続")
batch4 in Test := {
  Def.sequential(
    Def.task {
      "powershell.exe date" !
    } ,
    Def.task {
      (testOnly in Test).toTask(" somepackage.ValidationSpec").value
  ).value
}

6. プロジェクトにREPL代わりに書いたAppコードをsbtから呼びたいのよ→runMain

Hello.scala

package tools

object Hello extends App {
   println("Hello")
}
build.sbt

lazy val batch5 = taskKey[Unit]("内部ツール呼び出し")
batch5 in Test := (runMain in Test).toTask(" tools.Hello").value
(↑ toTask(" の部分のスペースが重要)

[ScalaTest] sbt内でタイムスタンプが使いたい

ログの書き出しなどでsbtで書き出しファイルにタイムスタンプを付けたいとか。

build.sbt

testOptions in Test += Tests.Argument(TestFrameworks.ScalaTest,"-fWDT", s"log${java.time.LocalDateTime.now().format(java.time.format.DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))}.txt" )

調べたものの結局は使わなかったのだが。。。sbtはまだ勘所がよくつかめぬ。

[Scala,ScalaTest]テスト実行内でテスト名を使う

ScalaTestでテストケースの中で、テストの名称を使う。

多用はしないだろうが、テストツール内でログを自力でレポート形式に荒加工する場合などに使えるかも。

SampleSpec.scala

class SampleSpec extends FreeSpec {
 "第一テスト" - {
  "第一項" in {
    val a = 1+1
    println(s"エビデンス:$a")
    assert(a == 2)
  }
 }
}

import org.scalatest.{FreeSpec, fixture}

class SampleSpec extends fixture.FreeSpec with fixture.TestDataFixture {
 "第一テスト" - {
  "第一項" in { td => {
      println(s"テスト「${td.scopes.mkString("/")}/${td.text}」)
      val a = 1+1
      println(s"エビデンス:$a")
      assert(a == 2)
     }
  }
 }
}

何も考えずにJoda-Time (Java, Scala)

sdt.

libraryDependencies += "joda-time" % "joda-time" % "2.9.9"

meven

<dependency>
  <groupId>joda-time</groupId>
  <artifactId>joda-time</artifactId>
  <version>2.9.9</version>
</dependency>

今または指定日時

val date = DateTime.now()
val date = new Date(2001,1,1,12,30,0)

足す、引く

val date = date.plusDays(1)
val date = date.minusYear(1)

書く

val str = date.toString()
val str = date.toString("yyyy/MM/dd HH:mm:ss")
//エスケープは ' で囲む 
val str = date.toString("yyyy-MM-dd'T'HH:mm:ss")
val date = DateTime.parse(str,DateTimeFormat.forPattern("yyyyMMdd"))

android の java.lang.VerifyError は悩ましい

先週やってた作業の中で突然androidアプリでjava.lang.VerifyErrorが出始めたケースがあった。結果から言うと根本原因はわからんかった。

解決方法としてはコールバック処理の一部のソースをリファクタリングしてメソッドとして外に移動 (Extract Method) させたら現象が消えたというていたらく。。。

何か作法としてあまりよくない書き方をしてたんだろうなとは思いつつも問題は解決せねばならぬ。

show must go on なのだ。。。

[android] androidxにプロジェクト移行したら、OssLicensesMenuActivity でエラー

調べたけどなかなかぴったり説明が出なかったので。

targetSdkVersion 27 を 28 にしてねwariningがよく出るようになってきたので 28にアップしたら、androidxにしなきゃダメみたいなメッセージが出てきたので、ライブラリの依存性をごりごり修正してたら、OSSライセンス一覧生成画面のところで以下のエラー

java.lang.IncompatibleClassChangeError: Class 'com.google.android.gms.oss.licenses.OssLicensesMenuActivity' does not implement interface 'androidx.lifecycle.LifecycleOwner' in call to 'androidx.lifecycle.Lifecycle androidx.lifecycle.LifecycleOwner.getLifecycle()' (declaration of 'androidx.lifecycle.LiveData' appears in ・・・

解決方法 以下をgradleに追加

implementation "androidx.appcompat:appcompat:1.1.0-rc01"

IntelliJ IDEAのキーボードショートカット一覧の機械翻訳

最近IntelliJ IDEAやAndroid Studioを使うことが多く、ショートカットに少し慣れてきたところなのだが、C:\Program Files\JetBrains\IntelliJ IDEA XXX\help (Windows版)にあるReferenceCard.pdfをざっくりGoogle translateに通したので誰か欲しい人もいるだろうから置いておきます。

Editing 編集
Ctrl + Space 基本コード補完(任意のクラス、メソッド、または変数の名前)
Ctrl + Shift + Space スマートコード補完(予想されるタイプでメソッドと変数のリストをフィルタリング)
Ctrl + Shift + Enter 完全なステートメント
Ctrl + P パラメータ情報(メソッド呼び出し引数内)Ctrl + Qクイックドキュメント検索
Shift + F1 外部文書
Ctrl + mouse over code 簡単な情報
Ctrl + F1 キャレットでエラーや警告の説明を表示する
Alt + Insert コードを生成…(Getters、Setters、Constructors、hashCode / equals、toString)
Ctrl + O メソッドをオーバーライドする
Ctrl + I メソッドを実装する
Ctrl + Alt + T …で囲む(if..else、try..catch、for、synchronizedなど)
Ctrl + / 行コメントによるコメント/コメント解除
Ctrl + Shift + / ブロックコメントでコメント/コメント解除
Ctrl + W 連続して増加するコードブロックを選択
Ctrl + Shift + W 現在の選択を前の状態に減らします
Alt + Q コンテキスト情報
Alt + Enter Show 意図的なアクションとクイックフィックス
Ctrl + Alt + L 再フォーマットコード
Ctrl + Alt + O インポートを最適化する
Ctrl + Alt + I 自動インデント行
Tab / Shift + Tab 選択行をインデント/未インデント
Ctrl + X , Shift + Delete 現在の行または選択したブロックをクリップボードに切り取ります
Ctrl + C , Ctrl + Insert 現在の行または選択したブロックをクリップボードにコピーする
Ctrl + V , Shift + Insert クリップボードから貼り付ける
Ctrl + Shift + V 最近のバッファから貼り付ける…
Ctrl + D 現在行または選択したブロックを複製する
Ctrl + Y キャレットの行を削除
Ctrl + Shift + J スマートライン参加
Ctrl + Enter スマートライン分割
Shift + Enter 改行する
Ctrl + Shift + U キャレットまたは選択したブロックの単語の大文字と小文字を切り替える
Ctrl + Shift + ]/[ コードブロックの終了/開始までを選択
Ctrl + Delete/Backspace 単語末尾まで削除
Ctrl + NumPad+/- コードブロックの展開/折りたたみ
Ctrl + Shift + NumPad+ すべて展開
Ctrl + Shift + NumPad- すべて折りたたむ
Ctrl + F4 アクティブなエディタタブを閉じる
Usage Search 利用検索
Alt + F7/Ctrl + F7 使用法の検索/ファイル内の使用法の検索
Ctrl + Shift + F7 ファイル内の使用法を強調表示する
Ctrl + Alt + F7 使い方を表示する

Navigation ナビゲーション

Ctrl + N 授業に行く
Ctrl + Shift + N ファイルへ
Ctrl + Alt + Shift + N シンボルへ
Alt + Right/Left 次/前のエディタタブに進む
F12 前のツールウィンドウに戻る
Esc (ツールウィンドウから)エディタに移動します
Shift + Esc アクティブウィンドウまたは最後のアクティブウィンドウを隠す
Ctrl + Shift + F4 アクティブな実行を閉じる/メッセージ/検索/ …タブ
Ctrl + G 行に移動
Ctrl + E 最近のファイルのポップアップ
Ctrl + Alt + Left/Right 前後に移動する
Ctrl + Shift + Backspace 最後の編集場所に移動します
Alt + F1 任意のビューで現在のファイルまたはシンボルを選択します
Ctrl + B , Ctrl + Click 宣言へ
Ctrl + Alt + B 実装に進む
Ctrl + Shift + I クイック定義ルックアップを開く
Ctrl + Shift + B 型宣言に行く
Ctrl + U スーパーメソッド/スーパークラスへ
Alt + Up/Down 前の/次の方法に進む
Ctrl + ]/[ コードブロックの末尾に移動
Ctrl + F12 ファイル構造ポップアップ
Ctrl + H 型の階層
Ctrl + Shift + H メソッド階層
Ctrl + Alt + H 呼び出し階層
F2 / Shift + F2 次/前の強調表示エラー
F4 / Ctrl + Enter ソースを編集/ソースを表示
Alt + Home ナビゲーションバーを表示
F11 ブックマークを切り替える
Ctrl + F11 ブックマークをニーモニックで切り替える
Ctrl + #[0-9] 番号付きブックマークに移動する
Shift + F11 ブックマークを表示
Search/Replace 検索/置換
Double Shift どこでも検索
Ctrl + F 見つける
F3 / Shift + F3 次を検索/前を検索
Ctrl + R 交換する
Ctrl + Shift + F パスで探す
Ctrl + Shift + R パスに置き換える
Ctrl + Shift + S 構造的に検索する(Ultimate Editionのみ)
Ctrl + Shift + M 構造的に交換する(Ultimate Editionのみ)

Live Templates ライブテンプレート

Ctrl + Alt + J ライブテンプレートで囲む
Ctrl + J ライブテンプレートを挿入
iter Java SDK 1.5スタイルに従った繰り返し
inst instanceofでオブジェクトタイプを確認し、それをダウンキャストします。
itco java.util.Collectionの要素を繰り返します
itit java.util.Iteratorの要素を繰り返します
itli java.util.Listの要素を繰り返します
psf パブリックスタティックファイナル
thr 新しく投げる

Refactoring リファクタリング

F5 コピーする
F6 移動する
Alt + Delete 安全な削除
Shift + F6 リネーム
Ctrl + F6 署名を変更
Ctrl + Alt + N 列をなして
Ctrl + Alt + M 抽出方法
Ctrl + Alt + V 変数を抽出する
Ctrl + Alt + F 抽出フィールド
Ctrl + Alt + C 定数を抽出する
Ctrl + Alt + P 抽出パラメータ

Debugging デバッグ

F8/F7 ステップオーバー/ステップイン
Shift + F7 / Shift + F8 スマートステップイン/ステップアウト
Alt + F9 カーソル位置まで実行
Alt + F8 式を評価する
F9 プログラムを再開
Ctrl + F8 ブレークポイントを切り替える
Ctrl + Shift + F8 ブレークポイントを表示する

Compile and Run コンパイルして実行する

Ctrl + F9 プロジェクトを作成します(修正および依存コンパイル)。
Ctrl + Shift + F9 選択したファイル、パッケージ、またはモジュールをコンパイルする
Alt + Shift + F10/F9 設定を選択して実行および/またはデバッグ
Shift + F10/F9 実行/デバッグ
Ctrl + Shift + F10 エディタからコンテキスト設定を実行する

VCS/Local History VCS /地域の歴史

Ctrl + K / Ctrl + T プロジェクトをVCSにコミット/ VCSからのアップデート
Alt + Shift + C 最近の変更を見る
Alt + BackQuote (`) 「VCS」クイックポップアップ

General 全般

Alt + #[0-9] 対応するツールウィンドウを開く
Ctrl + S すべてを救う
Ctrl + Alt + Y 同期する
Ctrl + Shift + F12 最大化エディタの切り替え
Alt + Shift + F お気に入りに追加
Alt + Shift + I 現在のプロファイルで現在のファイルを調べる
Ctrl + BackQuote (`) クイックスイッチ電流方式
Ctrl + Alt + S 設定ダイアログを開く
Ctrl + Alt + Shift + S プロジェクト構造ダイアログを開く
Ctrl + Shift + A アクションを探す
Ctrl + Tab タブとツールウィンドウを切り替える
jetbrains.com/idea blog.jetbrains.com/idea @intellijidea