20191124のJavaに関する記事は20件です。

Mavenで導入したKotlinのInterfaceに実装した関数を、Java8からデフォルト実装として利用する方法

概要

KotlinのI/Fにはデフォルト実装を定義できますが、Java側からそのデフォルト実装を利用するにはあらかじめ設定が必要です。
今回はその設定方法を書き記します。

前提

  • Maven
  • Kotlin 1.35
  • Java 1.8

設定方法

①Kotlinのプラグインの設定

pom.xmlのkotlinをcompileさせることを定義している箇所の引数に、-Xjvm-default=compatibility を追加
例:

pom.xml
<plugin>
    <groupId>org.jetbrains.kotlin</groupId>
    <artifactId>kotlin-maven-plugin</artifactId>
    <version>${kotlin.version}</version>
    <executions>
        <execution>
            <id>compile</id>
            <goals> <goal>compile</goal> </goals>
            <configuration>
                <sourceDirs>
                    <sourceDir>${project.basedir}/src/main/kotlin</sourceDir>
                    <sourceDir>${project.basedir}/src/main/java</sourceDir>
                </sourceDirs>
                <args>
                    <arg>-Xjvm-default=compatibility</arg>
                </args>
            </configuration>
        </execution>
        <execution>
            <id>test-compile</id>
            <goals> <goal>test-compile</goal> </goals>
            <configuration>
                <sourceDirs>
                    <sourceDir>${project.basedir}/src/test/kotlin</sourceDir>
                    <sourceDir>${project.basedir}/src/test/java</sourceDir>
                </sourceDirs>
                <args>
                    <arg>-Xjvm-default=compatibility</arg>
                </args>
            </configuration>
        </execution>
    </executions>
</plugin>

propertiesにjvmTargetを記載
例:

pom.xml
<properties>
    <jdk.version>1.8</jdk.version>
    <encoding>UTF-8</encoding>
    <kotlin.version>1.3.60</kotlin.version>
    <kotlin.compiler.jvmTarget>1.8</kotlin.compiler.jvmTarget>
</properties>

②Javaにデフォルト実装として認識させたいメソッドに @JvmDefault アノテーションを付与

以上となります。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Java】条件文の個人的まとめ(基礎)

【Java】条件文の個人的まとめ(基礎)

エンジニアの t-77 です。
個人的にJavaの勉強で大事だなと思ったことをまとめました。
今回は、条件文についてです。

① if文

  • if文は、条件がtrueだった場合に、そのブロック内の文を処理する。
  • elseを使うことで、設定した条件以外がtrueとして判定され、そのブロック内の文を処理する。
  • else ifを使うことで、複数の条件を設定可能。
sample.java
// numberの値によって、出力される言葉が異なる。
// number == 10 の場合、処理が実行される。
if (number == 10) {
  System.out.println("Wayne");
// number == 11 の場合、処理が実行される。
} else if (number == 11) {
  System.out.println("Ryan");
// numberが上記以外の場合、処理が実行される。
} else {
  System.out.println("other");
}

上記の例文の場合「出力処理」は、if文内で共通である。
したがって、下記の例文のように「出力処理」をまとめたほうが修正・保守しやすい。

sample2.java
// numberの値によって、出力される言葉が異なる。
// 変数nameを定義する。
String name;
if (number == 10) {
  name = "Wayne";
} else if (number == 11) {
  name = "Ryan";
} else {
  name = "other";
}
// numberによってnameに代入された文字列が出力される。
System.out.println(name);

② switch文

  • switch文は、caseの値に一致した場合、その文を処理する。
  • どれにも一致しなかった場合、defaultの文を処理する。
  • defaultは、省略することも可能。
  • 処理を終了させるために、「break」を必ず書く。
sample.java
// numberの値によって、出力される言葉が異なる。
// caseの値が10の場合、処理が実行される。
switch (number) {
  case 10:
  System.out.println("Wayne");
  break;
// caseの値が11の場合、処理が実行される。
  case 11:
  System.out.println("Ryan");
  break;
// 値が上記以外の場合、defaultの処理が実行される。
  default:
  System.out.println("other");
  break;
}

下記のように、複数のcaseで同じ処理を行わせることも可能である。

sample2.java
// numberの値によって、出力される言葉が異なる。
// caseの値が1、2の場合、処理が実行される。
switch (number) {
  case 1:
  case 2:
  System.out.println("低評価");
  break;
// caseの値が3の場合、処理が実行される。
  case 3:
  System.out.println("及第点");
  break;
// caseの値が4、5の場合、処理が実行される。
  case 4:
  case 5:
  System.out.println("高評価");
  break;
// 値が上記以外の場合、defaultの処理が実行される。
  default:
  System.out.println("評価なし");
  break;
}

③ while文

  • while文は、条件がfalseになるまでブロック内の文を繰り返し処理する。
  • falseになるように条件を記述しないと、ブロック内が永遠にループしてしまうため注意する。
sample.java
// 初期値を設定する。
int i = 1;
// falseになるまで、文を繰り返し処理する。
while (i <= 11) {
  System.out.println("背番号は、" + i + "番だ");
  // iを増やすことで、永遠にループしないようにする。
  i++;
}

④ do~while文

  • do~while文は、条件がfalseになるまでブロック内の文を繰り返し処理する。
  • while文が最初の繰り返し前に条件がfalseであれば、処理が行われないのに対して、do~while文は、少なくとも1回は繰り返し処理を行い、その後条件がfalseになるまで繰り返し処理を行う。
sample.java
// 初期値を設定する。
int i = 1;
// doブロック内の文を処理する。
do {
  System.out.println("背番号は、" + i + "番だ");
  // iを増やすことで、永遠にループしないようにする。
  i++;
// falseになるまで、文を繰り返し処理する。
} while (i <= 11) ;

⑤ 文のネスト

  • 文は、ネストする(入れ子にする)ことができる。
  • ネストすることで、より複雑な記述ができる。
  • ネストしすぎるとコードが見ずらくなるため、複数の人と共同で開発する場合には切り分けられる部分は切り分けたほうが良い。
sample.java
// for文の中にfor文のような記述が可能。
// if文など異なる条件文を組み合わせることも可能。
for (int i = 1; i <= 11; i++) {
  System.out.println("背番号が" + i + "番の選手だ。");
  for (int s = 1; s <= 5; s++) {
    System.out.println(s + "回目のシュートを打った。");
    if (s == 3) {
      System.out.println("シュートを決めた。");
    }
  }
}

⑥ break文

  • break文は、処理の流れを強制的に終了させ、ブロックから抜けることができる。
  • switch文では、breakが必須。
sample.java
// i = 8の場合、処理が終了する。
for (int i = 1; i <= 11; i++) {
  System.out.println("背番号が" + i + "番の選手だ。");
  if (i == 8) {
    System.out.println("処理を終了します。");
    break;
  }
}
console
背番号が1番の選手だ。
背番号が2番の選手だ。
背番号が3番の選手だ。
背番号が4番の選手だ。
背番号が5番の選手だ。
背番号が6番の選手だ。
背番号が7番の選手だ。
処理を終了します。

i = 8になった時に、breakによって処理が終了されている。

⑦ continue文

  • continue文は、繰り返し内の処理をスキップし、ブロックの先頭に戻り、次の処理に入ることができる。
sample.java
// i = 8の場合、処理をスキップし、次の処理に入る。
for (int i = 1; i <= 11; i++) {
  System.out.println("背番号が" + i + "番の選手だ。");
  if (i == 8) {
    continue;
  }
}
console
背番号が1番の選手だ。
背番号が2番の選手だ。
背番号が3番の選手だ。
背番号が4番の選手だ。
背番号が5番の選手だ。
背番号が6番の選手だ。
背番号が7番の選手だ。
背番号が9番の選手だ。
背番号が10番の選手だ。
背番号が11番の選手だ。

i = 8になった時に、continueによって処理がスキップされている。

Javaの勉強の一環でしたが、条件文は、JavaScriptなど他の言語でも使用されるため、基礎をもう一度見直す良い機会となりました。
以上になります。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Spring フラッシュスコープを利用したリダイレクトと単体テスト

あるエンドポイントから別のエンドポイントにリダイレクトさせる際に、フラッシュスコープを利用するとリダイレクト先へパラメータを引き継がせることができる。

コントローラ

以下のコントローラで/fromにGETでアクセスした際に、/toへリダイレクトさせる。
リダイレクト元の処理で、bookNamebuyerListをフラッシュスコープに突っ込んで、リダイレクト先へ引き継がせる。

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import java.util.Arrays;
import java.util.List;

@Controller
public class DemoController {

    @GetMapping(path = "/from")
    public String from(RedirectAttributes redirectAttributes) {

        redirectAttributes.addFlashAttribute("bookName", "ドラえもん");

        List<String> buyerList = Arrays.asList("野比", "剛田");
        redirectAttributes.addFlashAttribute("buyerList", buyerList);

        return "redirect:/to";
    }

    @GetMapping(path = "/to")
    public String to(Model model) {
        System.out.println(model);
        return "demo";
    }
}

/fromにアクセスすると/toにリダイレクトされ、引き継がれたパラメータがコンソールに表示される。

{bookName=ドラえもん, buyerList=[野比, 剛田]}

ポイント

  • リダイレクト元のメソッドでRedirectAttributesを引数に設定し、.addFlashAttributeで引き継ぐattribute名と内容を指定する。なお、attribute名を省略することもできるが、その場合はクラス名に応じてattribute名が自動設定される。
  • リダイレクト先では引数に設定したModelの中にattributeが含まれている。内容を参照したい場合は.getAttributeでattribute名を指定する。

単体テスト

/fromにアクセスした際に、パラメータを引き継いで/toへリダイレクトされることを確認する。

package com.example.demo.controller;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;

import java.util.Arrays;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.flash;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class DemoControllerTest {

    @Autowired
    MockMvc mockMvc;

    @Test
    public void redirectWithAttributes() throws Exception {
        mockMvc.perform(get("/from"))
                .andExpect(status().isFound())
                .andExpect(redirectedUrl("/to"))
                .andExpect(flash().attribute("bookName", "ドラえもん"))
                .andExpect(flash().attribute("buyerList", Arrays.asList("野比", "剛田")));
    }
}

ポイント

  • andExpect()で期待値通りに動いていることを確認。
  • status().isFound()でHTTPステータスが302であることを確認。
  • redirectedUrl()でリダイレクト先のパスをアサーション。
  • flash().attribute()で引き継ぐパラメータのアサーション。

FlashMapによる取り出し

以下のように引き継ぎパラメータをFlashMapで取り出すことができる。余計なパラメータが入っていないことの確認や、単純な文字列表現ではアサーションできない場合はこちらを利用する。

    @Test
    public void redirectWithAttributes() throws Exception {

        MvcResult mvcResult = mockMvc.perform(get("/from"))
                .andExpect(status().isFound())
                .andExpect(redirectedUrl("/to"))
                .andReturn();

        FlashMap flashMap = mvcResult.getFlashMap();

        assertThat(flashMap.size()).isEqualTo(2);
        assertThat(flashMap.get("bookName")).isEqualTo("ドラえもん");
        assertThat(flashMap.get("buyerList")).isEqualTo(Arrays.asList("野比", "剛田"));
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Androidで文字を点滅するメソッドができてうれしい

 こんにちはヨースケです。文字列の一致に"=="を使っていました(笑)。equalsですね!

初めて作ったアプリで実装

 この文字を点滅させるメソッドは、初めてアプリを公開した時に実装したものになります。計算問題付き電卓←このアプリは名前の通り電卓に計算問題を出す機能を付けたアプリとなっています。計算問題がスタートするとタイマーをスタートさせ、全10問クリアするとタイマーがストップしそのタイムで点滅するようにしました。また、答え終わると過去のクリアタイムを表示するようにしています。

ソースコード

 あんまり長ったらしくなってもよくないので稚拙なコードではありますが自分自身の備忘録として貼っておきます。

Quest.java
public class Quest extends AppCompatActivity {
private final Handler handler = new Handler();
//タイマーを表示
    private TextView timerText;
...中略...
//全問正解するとtimer_stop()を呼びます
//タイマーストップ
        public void timer_stop() {
            handler.removeCallbacks(runnable);
            timerText.setTextColor(Color.RED);
            count = 0;
            insert_db();
            result_tweet();
            init();
        }

 //止めたタイムをチカチカさせるメソッド
        private void init() {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    int time = 500;
                    try {
                        Thread.sleep(time);
                    } catch (Exception e) {

                    }
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            if (timerText.getVisibility() == View.VISIBLE) {
                                timerText.setVisibility(View.INVISIBLE);
                            } else {
                                timerText.setVisibility(View.VISIBLE);
                            }
                            init();
                        }
                    });
                }
            }).start();
        }
}

 このinit()メソッドで止めたタイマーをチカチカ点滅させています。この→テキストビューを点滅させる方法を参考にプログラムを組んだので大体の流れしか分からず、Threadやhandlerのところも勉強不足なのでまだまだ勉強が足りないなぁーと反省。(メソッド名ェ...)

 現在は3つ目のアプリ、「一日日記(福岡編)」(仮題)を開発中です。ひと段落着いたときや投稿したいなーとなったときに書いていくのでよろしくお願いしますね(^ ^)。ちなみにTwitterもやっていますので気軽にフォローよろしくお願いします!→Twitterのアカウント

参考URL:
テキストビューを点滅させる方法:https://codeday.me/jp/qa/20190108/126818.html

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Cloud Firestore を Java から使う際のドキュメントまとめ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

再帰下降構文解析法による数式の構文解析器の実装(Java)

職場のネット寸断が激しかったので久しぶりのネット活動です
タイトルは今のQiitaではあまり受けが良くなさそうな硬そうな命名にしてみました

要件で求められるパフォーマンスの関係で、再帰下降構文解析法で四則計算する解析器を作成しました
誰かの参考になるかもしれないので置いておきます

要件

バッチアプリで数式が文字列で取得されるので、それを計算して結果を返却したかったらしいのですが、その数が膨大でした
元々は、JavaからJavaScriptを呼び出して数式をそのまま渡して計算してもらうつもりだったそうなのですが、対象の式の数が何万何十万単位のため1日で終了しなかったようです

しかもこれからもどんどん増えるそうなのです!

当然次のジョブもあるので、その開始時間までに処理が終わるようにしなければなりません

そこで僕に何とかしてほしいと話がまわってきました
最初に話を聞いたときは、外部プログラム呼び出しでしかもJavaScriptで高速な計算処理ができるわけないがな、と思いましたが

ようは、高速な四則計算が可能な解析器を作成すればよいわけです
除算時の丸め誤差も制御したいらしいので、それも考える必要があります

まあ全部Javaで実装すれば速度面は問題ないだろうと予想して、取り合えず単純な実装のわりにそこそこ強力な再帰下降構文解析法でいくことに決めました

コード

以下Java8で実装しています

FormulaParser.java
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class FormulaParser {
    private static final int HIGH_PRIORITY = 3;
    private static final int MID_PRIORITY = 2;
    private static final int LOW_PRIORITY = 1;
    private static final Pattern EMPTY_BRACKET = Pattern.compile("\\(\\)");
    private static final Pattern NOT_CLOSE_BRACKET = Pattern.compile("\\)\\(");
    private static final int NO_EXIST = -1;
    private static final String ZERO = "0";
    private static final String BRACKET_AND_ZERO = "(0";

    private String expression; // 式
    private FormulaParser left; // 左子ノード
    private FormulaParser right; // 右子ノード
    private int scale;

    /**
     * スケールありコンストラクタ
     * ※除算の場合のみscaleを考慮する
     *
     * @param expression 計算式
     * @param scale      除算時切り落とし小数点位置
     */
    public FormulaParser(String expression, int scale) {
        this.expression = format(expression);
        this.scale = scale;
    }

    /**
     * 式を二分木に分割し計算する
     *
     * @throws FormulaParseException
     */
    public String execute() throws FormulaParseException {
        // 最も外側の括弧を消す
        String exp = delMostOuterBrackets(this.expression);
        // 式から演算子を探して位置を取得
        int opePos = getOpePos(exp);
        // 式に演算子が含まれないなら項であるとみなす
        if (opePos < 0) {
            left = null;
            right = null;
            return new BigDecimal(exp).toPlainString();
        }
        // 演算子位置が式の先頭か末尾の場合、不正扱い
        if (opePos == 0 || opePos == exp.length() - 1) {
            throw new FormulaParseException(exp);
        }
        // 演算子の左側を左の部分式としてノードを作成
        left = new FormulaParser(exp.substring(0, opePos), scale);
        // 演算子の右側を右の部分式としてノードを作成
        right = new FormulaParser(exp.substring(opePos + 1), scale);
        // 残った演算子をこのノートに設定
        expression = exp.substring(opePos, opePos + 1);
        return calculate(left.execute(), OPERATOR.getEnum(expression), right.execute(), scale);
    }

    /**
     * 演算子の位置を取得
     *
     * @param exp 式
     * @return 演算子位置(0始まり)/ない場合は-1を返す
     */
    private static int getOpePos(String exp) {
        int opePos = NO_EXIST;
        int curPriority = Integer.MAX_VALUE;
        int nest = 0;
        for (int i = 0, len = exp.length(); i < len; i++) {
            int priority = 0;
            switch (OPERATOR.getEnum(String.valueOf(exp.charAt(i)))) {
                case PLUS:
                case MINUS:
                    priority = MID_PRIORITY;
                    break;
                case MULTIPLY:
                case DIVIDE:
                    priority = HIGH_PRIORITY;
                    break;
                case LEFT_BRACKET:
                    nest++;
                    continue;
                case RIGHT_BRACKET:
                    nest--;
                    continue;
                default:
                    continue;
            }
            if (nest == 0 && priority <= curPriority) {
                curPriority = priority;
                opePos = i;
            }
        }
        return opePos;
    }

    /**
     * 計算処理
     *
     * @param lExp  左項
     * @param ope   演算子
     * @param rExp  右項
     * @param scale スケール
     */
    private String calculate(String lExp, OPERATOR ope, String rExp, int scale) throws FormulaParseException {
        BigDecimal left = new BigDecimal(lExp);
        BigDecimal right = new BigDecimal(rExp);
        switch (ope) {
            case PLUS:
                return left.add(right).toPlainString();
            case MINUS:
                return left.subtract(right).toPlainString();
            case MULTIPLY:
                return left.multiply(right).toPlainString();
            case DIVIDE:
                return left.divide(right, scale, RoundingMode.DOWN).toPlainString();
            default:
                throw new FormulaParseException(left + ope.val + rExp);
        }
    }

    /**
     * フォーマット
     * 半角スペースは排除
     * 正負記号と加減算演算子の区別がつけ難いため、0埋めして簡単にする
     *
     * @param exp 式
     * @return フォーマットされた式 ex) -3 + 1 → 0-3+1
     */
    private static String format(String exp) {
        // 半角スペースは排除する
        StringBuilder e = new StringBuilder(exp.replaceAll(" ", ""));
        int cur = 0;
        for (; ; ) {
            int plusIndex = e.indexOf(OPERATOR.PLUS.val, cur);
            int minusIndex = e.indexOf(OPERATOR.MINUS.val, cur);
            if (plusIndex == NO_EXIST && minusIndex == NO_EXIST) {
                break;
            }
            // 演算子が存在しかつ前にある方をのインデックスを取得
            if (plusIndex < minusIndex) {
                cur = (plusIndex == NO_EXIST) ? minusIndex : plusIndex;
            } else {
                cur = (minusIndex == NO_EXIST) ? plusIndex : minusIndex;
            }
            if (cur == 0) {
                e.insert(cur, ZERO);
                cur = cur + ZERO.length() + 1;
                continue;
            }
            // 演算子の一つ前の文字が数値ではなく、閉じ括弧や乗算、除算演算子でもない場合0を追加
            char preChar = e.charAt(cur - 1);
            if (!Character.isDigit(preChar)
                    && preChar != OPERATOR.RIGHT_BRACKET.cVal) {
                if (preChar == OPERATOR.MULTIPLY.cVal
                        || preChar == OPERATOR.DIVIDE.cVal
                        || preChar == OPERATOR.MINUS.cVal) {
                    int endDigits = 0;
                    for (int i = cur + 1, len = e.length(); i < len; i++) {
                        char c = e.charAt(i);
                        endDigits = i;
                        if (!Character.isDigit(c) && c != '.') {
                            break;
                        }
                    }
                    e.insert(cur, BRACKET_AND_ZERO).insert(endDigits + BRACKET_AND_ZERO.length() + 1, OPERATOR.RIGHT_BRACKET.cVal);
                    cur = endDigits + BRACKET_AND_ZERO.length();
                } else {
                    e.insert(cur, ZERO);
                    cur = cur + ZERO.length();
                }
            }
            cur++;
            if (cur > e.length()) break;
        }
        return e.toString();
    }

    /**
     * 丸括弧削除処理
     * 式から最も外側の括弧を削除する
     * (1+2)+(3+4)のような式はそのまま返す
     *  括弧が閉じていないなら不正な式として例外をスロー
     *
     * @param exp 式
     * @return 丸括弧を削除した式 ex) (4*2) → 4*2,  ((3+4)) → 3+4, (1+2)+(3+4) → (1+2)+(3+4)
     * @throws FormulaParseException
     */
    private static String delMostOuterBrackets(String exp) throws FormulaParseException {
        if (matchFirst(exp, EMPTY_BRACKET).isPresent()) throw new FormulaParseException(exp);
        if (matchFirst(exp, NOT_CLOSE_BRACKET).isPresent()) throw new FormulaParseException(exp);
        if (exp.charAt(0) == OPERATOR.RIGHT_BRACKET.cVal) throw new FormulaParseException(exp);

        boolean hasMostOuterBrackets = false;
        int nest = 0;
        // 1文字目を検証
        if (exp.charAt(0) == OPERATOR.LEFT_BRACKET.cVal) {
            hasMostOuterBrackets = true;
            nest++;
        }
        // 1文字目以降を1文字ずつずつ検証
        for (int i = 1, len = exp.length(); i < len; i++) {
            if (exp.charAt(i) == OPERATOR.LEFT_BRACKET.cVal) {
                nest++;
            } else if (exp.charAt(i) == OPERATOR.RIGHT_BRACKET.cVal) {
                nest--;
                if (nest == 0 && i < len - 1) {
                    hasMostOuterBrackets = false;
                }
            }
        }
        // 括弧が閉じていないなら不正な式
        if (nest != 0) throw new FormulaParseException(exp);
        // 括弧がないならそのまま返す
        if (!hasMostOuterBrackets) return exp;
        // 最初と最後の括弧を取り除く
        String ret = exp.substring(1, exp.length() - 1);
        // まだ括弧があるならもう一度処理
        if (ret.charAt(0) == OPERATOR.LEFT_BRACKET.cVal
                && ret.charAt(ret.length() - 1) == OPERATOR.RIGHT_BRACKET.cVal) {
            return delMostOuterBrackets(ret);
        }
        return ret;
    }

    /**
     * 検索
     *
     * @param s 検索対象
     * @param p 正規表現
     * @return 検索結果
     */
    private static Optional<String> matchFirst(String s, Pattern p) {
        Matcher m = p.matcher(s);
        return m.find() ? Optional.of(m.group(0)) : Optional.empty();
    }

    /**
     * 演算子
     */
    private enum OPERATOR {
        PLUS("+", '+'),
        MINUS("-", '-'),
        MULTIPLY("*", '*'),
        DIVIDE("/", '/'),
        LEFT_BRACKET("(", '('),
        RIGHT_BRACKET(")", ')'),
        NO_OPE(" ", ' ');

        public String val;
        public char cVal;
        private final static Map<String, OPERATOR> m = new HashMap<>();

        static {
            for (OPERATOR o : OPERATOR.values()) {
                m.put(o.val, o);
            }
        }

        private OPERATOR(String val, char cVal) {
            this.val = val;
            this.cVal = cVal;
        }

        public static OPERATOR getEnum(String val) {
            return m.getOrDefault(val, NO_OPE);
        }
    }
}
FormulaParseException.java
public class FormulaParseException extends Exception {
    public FormulaParseException(String msg) {
        super(msg);
    }
}

例外はそれっぽいのを適当に実装しました
パーサによる解析と、計算処理は分けずに一つのクラスにまとめています
正負記号と加減演算子の区別が面倒なので0埋めしてます
必要に応じて括弧も追加しているのもポイントです

解析もそれなりにコストがかかるので、本来のアプリは事前チェックもそれなりに手厚くしています
あと、Math関数や変数も処理できるようにしているのですが、思い出してコードを書くのがしんどいので省きました
matchFirst()メソッドはその名残ですね
他にも便利なユーティリティーを実装して色々やっていました

変数は適当に置換すればいいだけです
Math関数についてはヒントだけ言うと、↑のコードだとMath関数は項として扱われます
なので下記のコードあたりに、Math関数か判別してMath関数ならそれを処理するメソッドを呼び出すようにすればいいです
return new BigDecimal(exp).toPlainString();

Math関数を処理するメソッドは、delMostOuterBrackets()format()みたいに地道に文字をチェックして処理すればいいです
つまり、記事に書いたコードを応用すればいけるので必要なら自力で作ってみてください

終わりに

とりあえず実装して動かしてみたら要件を満たしてくれたのでそこで終了です
件数が何千万から億までいくと怪しいですが、そこまではいかないそうなので大丈夫のようです

テストは山ほど書きましたが、思い出して移植するのがしんどいので省きます
まあ式の想定結果をチェックするだけなのでJUnitも不要なぐらいですが

僕はCS専攻ではなく独学で学んでいる口なので、実務でこの手の設計実装をじっくり考えるのは結構ためになりました

またアルゴリズムとデータ構造の知識がないと太刀打ちできないような案件がでてきてくれることを祈って記事は終わりです

ノシ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Java11インストール後にEclipseが起動しなくなる現象

はじめに

職場でJava11をインストールしたところ、Eclipseが起動しなくなりました。
当初は原因が全く分からずに苦戦したので、記録として記事を書くことにしました。

状況

当初の状態

以下のように古いJavaであったため、空いた時間でJavaのバージョンを上げることにしました。
なお、EclipseはPleiadesの日本語化版を使っています。

  • OS:Windows 10 Pro
  • Java:Oracle Java 8
  • Eclipse:Eclipse 4.6(Neon)

Javaのバージョンアップ後の状態

無償で使えて簡単にインストールできる「Amazon Corretto」をインストールしました。

発生した不具合

Eclipseのスプラッシュ画面が表示された後、An error has occurred. See the log file ...というダイアログが出てしまい、Eclipseが起動しませんでした。

eclipse_error.png

対応策

  • 以下の記事を参考にして、最新版のEclipseをインストールしたところ、無事にEclipseが起動しました。
  • ちなみに、今回インストールしたEclipseのバージョンは「リリース 2019-09」の最新版です。

まとめ

  • Java 7からJava 8にバージョンアップした時には、Eclipseが起動しないという症状はなかったと思うので、今回はかなり驚きました。
  • 当初はAmazon Correttoに不具合があるのでは...と思いましたが、色々調べていくうちにJava10以降で同様の症状が出ていると分かり、最終的に比較的簡単な方法で解決できて良かったです。
  • バージョン管理に載せていないちょっとしたプログラムが幾つかありましたが、既存のワークスペースからコードを簡単にインポートできたので助かりました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

列挙型と switch 文の進化形!?Java で代数的データ型とパターンマッチングを実現してみる

Mikatus Advent Calendar 2019 8日目の記事。

Reactive in practice: A complete guide to event-driven systems development in Java を読んでいたら、The algebraic data type pattern(代数的データ型パターン)なんてものが出てきて面白かったのでメモっとく。

例題:株取引システムにおける注文種別を表現する

Reactive in practice には株取引システム Stock Trader における注文の種類を表現する方法が例題として掲載されている。株を取引したことがある人は知っていると思うのだが、株の注文には下記のような種類がある。

  • 成行 (market)
  • 指値 (limit)
  • 逆指値 (stop limit)

これらの注文を表現するために、Reactive in practice で代数的データ型パターンと呼んでいるデザインパターンが使われている。なぜ列挙型 (Enum) ではないのか?その謎を紐解いていこう。

実験用の Gradle プロジェクトを生成する

Lombok を使いたかったりもするので JShell ではなく Gradle プロジェクトで実験する。下記のコマンドで Gradle プロジェクトを生成しよう。

$ mkdir stock-trader
$ cd stock-trader
$ gradle init \
    --type java-library \
    --dsl groovy \
    --test-framework junit \
    --project-name stock-trader \
    --package com.example

株取引の注文種別を列挙型として実装する

まずは成行注文と指値注文のみを列挙型として実装してみよう。指値注文は指値 (limit price) を保持する必要があるので、列挙型に limitPrice フィールドを持たせる。ゲッターとなる getLimitPrice メソッドとセッターとなる setLimitPrice メソッドも定義する。注文が実行可能かを判定する isExecutable 抽象メソッドを定義して、各列挙値でオーバーライドしている。

src/main/java/com/example/order/OrderType.java
package com.example.order;

public enum OrderType {
    MARKET {
        @Override
        public boolean isExecutable(int currentPrice) {
            return true;
        }
    },
    LIMIT {
        @Override
        public boolean isExecutable(int currentPrice) {
            return currentPrice <= getLimitPrice();
        }
    };

    private int limitPrice;

    public int getLimitPrice() {
        return limitPrice;
    }

    public void setLimitPrice(int limitPrice) {
        this.limitPrice = limitPrice;
    }

    public abstract boolean isExecutable(int currentPrice);
}

この OrderType 列挙型を使用するテストを書いてみよう。

src/test/java/com/example/order/OrderTypeTest.java
package com.example.order;

import org.junit.Test;
import static org.junit.Assert.*;

public class OrderTypeTest {
    @Test
    public void testIsExecutableOnMarketOrder() {
        OrderType orderType = OrderType.MARKET;

        assertTrue(orderType.isExecutable(100));
    }

    @Test
    public void testIsExecutableOnLimitOrder() {
        OrderType orderType = OrderType.LIMIT;
        orderType.setLimitPrice(100);

        assertTrue(orderType.isExecutable(100));
    }
}

実際のところ株取引の注文種別を列挙型で実装することには下記のような問題がある。

  • 成行注文で setLimitPrice メソッドによって指値の指定ができる
  • 指値注文で指値なしの状態が実現できる

これらの問題の解決策は様々あるかもしれないが、今回は代数的データ型パターンを用いて解決してみようと思う。

シールドクラスパターンを用いて株取引の注文種別を代数的データ型として実装する

シールドクラスパターン (Sealed Class Pattern) として Maybe in Java という記事が Reactive in practice で参照されている。そのシールドクラスパターンで成行注文と指値注文のみを代数的データ型として実装してみよう。

src/main/java/com/example/order/OrderType.java
package com.example.order;

public abstract class OrderType {
    private OrderType() {
    }

    public static final class Market extends OrderType {
        @Override
        public boolean isExecutable(int currentPrice) {
            return true;
        }
    }

    public static final class Limit extends OrderType {
        private int limitPrice;

        public Limit(int limitPrice) {
            this.limitPrice = limitPrice;
        }

        public int getLimitPrice() {
            return limitPrice;
        }

        @Override
        public boolean isExecutable(int currentPrice) {
            return currentPrice <= limitPrice;
        }
    }

    public abstract boolean isExecutable(int currentPrice);
}

ここで OrderType クラスについていくつか補足しておこう。OrderType を抽象クラスとして宣言し、プライベートコンストラクタを定義することで、OrderType を継承できるクラスを OrderType の内部クラスに限定している。また、Market クラスと Limit クラスを final クラスとして宣言することで、Market クラスと Limit クラスを継承できないようにしている。これシールドクラスパターンと呼ばれる理由だと思う。

この OrderType クラスを使用するテストを書いてみよう。

src/test/java/com/example/order/OrderTypeTest.java
package com.example.order;

import org.junit.Test;
import static org.junit.Assert.*;

public class OrderTypeTest {
    @Test
    public void testIsExecutableOnMarketOrder() {
        OrderType orderType = new OrderType.Market();

        assertTrue(orderType.isExecutable(100));
    }

    @Test
    public void testIsExecutableOnLimitOrder() {
        OrderType orderType = new OrderType.Limit(100);

        assertTrue(orderType.isExecutable(100));
    }
}

株取引の注文種別を列挙型で実装することで生じる下記の問題は、代数的データ型で実装することで解決できた。

  • 成行注文で setLimitPrice メソッドによって指値の指定ができる
  • 指値注文で指値なしの状態が実現できる

その一方、注文が実行できるかどうかの判定はそもそも注文種別の責務なのかという問題がある。これは、列挙型と代数的データ型でそれぞれ例示した実装に共通する問題になる。注文が実行できるかどうかの判定かどうかを問わず、OrderType クラスの外で、下記のように条件分岐を実現したいことがあるのではないだろうか?

    @Test
    public void testSwitchOnLimitOrder() {
        OrderType orderType = OrderType.LIMIT;
        orderType.setLimitPrice(100);
        int currentPrice = 100;

        boolean result = false;
        switch (orderType) {
            case MARKET:
                result = true;
                break;
            case LIMIT:
                result = currentPrice <= orderType.getLimitPrice();
                break;
            default:
                throw new UnsupportedOperationException("Unsupported order type");
        }

        assertTrue(result);
    }
    @Test
    public void testIfOnLimitOrder() {
        OrderType orderType = new OrderType.Limit(100);
        int currentPrice = 100;

        boolean result = false;
        if (orderType instanceof OrderType.Market) {
            result = true;
        } else if (orderType instanceof OrderType.Limit) {
            result = currentPrice <= orderType.getLimitPrice();
        } else {
            throw new UnsupportedOperationException("Unsupported order type");
        }

        assertTrue(result);
    }

このような条件分岐には共通して下記のような問題がある。

  • OrderType クラスに新しい型を追加した場合、すべての OrderType についての条件分岐を探し出して修正する必要がある

この問題を解決するために、Visitor パターンを用いたパターンマッチングを導入してみよう。

Visitor パターンを用いたパターンマッチングを実装する

便宜的にパターンマッチングと呼んでいるが、Visitor パターンを適用するだけなので Scala のようなパターンマッチングを期待しないでほしい。また、isExecutable メソッドはコードを理解しやすくするために削除することにする。

src/test/java/com/example/order/OrderTypeTest.java
package com.example.order;

public abstract class OrderType {
    private OrderType() {
    }

    public static final class Market extends OrderType {
        @Override
        public <T> T match(CaseBlock<T> caseBlock) {
            return caseBlock._case(this);
        }
    }

    public static final class Limit extends OrderType {
        private int limitPrice;

        public Limit(int limitPrice) {
            this.limitPrice = limitPrice;
        }

        public int getLimitPrice() {
            return limitPrice;
        }

        @Override
        public <T> T match(CaseBlock<T> caseBlock) {
            return caseBlock._case(this);
        }
    }

    public interface CaseBlock<T> {
        T _case(Market market);
        T _case(Limit limit);
    }

    public abstract <T> T match(CaseBlock<T> caseBlock);
}

OrderType 抽象クラスに match 抽象メソッドを宣言し、それぞれの型で実装している。その match メソッドは CaseBlock インターフェイスを実装したクラスのインスタンスを受け取り、その _case メソッドを呼び出している。Visitor パターンを改めて説明する必要はないと思うが、ここの _case メソッドがオーバーロードされていることによって、それぞれの型によって処理が分岐することになる。蛇足だが、_case という名前にしているのは、case が予約語で使えないからだ。

この OrderType クラスを使用するテストを書いてみよう。使い方を見た方が理解しやすいと思う。

src/test/java/com/example/order/OrderTypeTest.java
package com.example.order;

import org.junit.Test;
import static org.junit.Assert.*;

public class OrderTypeTest {
    @Test
    public void testPatternMatchingOnLimitOrder() {
        OrderType orderType = new OrderType.Limit(100);
        int currentPrice = 100;

        boolean result = orderType.match(new OrderType.CaseBlock<>() {
            @Override
            public Boolean _case(OrderType.Market market) {
                return true;
            }

            @Override
            public Boolean _case(OrderType.Limit limit) {
                return currentPrice <= limit.getLimitPrice();
            }
        });

        assertTrue(result);
    }
}

これで下記の問題は解決される。

  • OrderType クラスに新しい型を追加した場合、すべての OrderType についての条件分岐を探し出して修正する必要がある

OrderType クラスに新しい型を追加した場合、もちろんコードの修正はしなければならない。しかしながら、条件分岐を上記のようなパターンマッチングで記述している限りは、コンパイル時にエラーが発生するようになるので、修正箇所の特定が容易になり、修正漏れを防ぐことができる。

Lombok で代数的データ型を洗練する

さて、代数的データ型パターンは便利そうだということがわかった。しかしながら、いかんせん定義するのが面倒でコードの見通しも良くない。その点を少し改善するために、ここでは Lombok を導入してみよう。

build.gradle ファイルに下記の行を追加する。

--- a/build.gradle
+++ b/build.gradle
@@ -9,6 +9,7 @@
 plugins {
     // Apply the java-library plugin to add support for Java Library
     id 'java-library'
+    id "io.freefair.lombok" version "4.1.5"
 }

 repositories {

Lombok を使用して書くとこうなる。

src/test/java/com/example/order/OrderTypeTest.java
package com.example.order;

import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.Value;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public abstract class OrderType {
    @Value
    @EqualsAndHashCode(callSuper = false)
    public static class Market extends OrderType {
        @Override
        public <T> T match(CaseBlock<T> caseBlock) {
            return caseBlock._case(this);
        }
    }

    @Value
    @EqualsAndHashCode(callSuper = false)
    public static class Limit extends OrderType {
        int limitPrice;

        @Override
        public <T> T match(CaseBlock<T> caseBlock) {
            return caseBlock._case(this);
        }
    }

    public interface CaseBlock<T> {
        T _case(Market market);
        T _case(Limit limit);
    }

    public abstract <T> T match(CaseBlock<T> caseBlock);
}

プライベートコンストラクタの定義を NoArgsConstructor アノテーションで実現する。取引種別は値オブジェクトを生成するクラスとして Value アノテーションを付与する。これで Market クラスも Limit クラスも final クラスになる。Limit クラスのフィールドの宣言を簡潔にしているが、フィールドはすべて private final になるし、コンストラクターとゲッターも自動的に生成される。なお EqualsAndHashCode アノテーションは、EqualsAndHashCode アノテーションを明示しなさいという警告が発生するので付与している。

スタティックコンストラクターを用意する

ここからは好みの問題だと思うけど、スタティックコンストラクターを用意するとよりそれっぽくなる。

src/test/java/com/example/order/OrderTypeTest.java
package com.example.order;

import com.google.errorprone.annotations.Var;
import lombok.*;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public abstract class OrderType {
    @Value
    @EqualsAndHashCode(callSuper = false)
    @AllArgsConstructor(access = AccessLevel.PROTECTED)
    public static class Market extends OrderType {
        @Override
        public <T> T match(CaseBlock<T> caseBlock) {
            return caseBlock._case(this);
        }
    }

    public static OrderType market() {
        return new Market();
    }

    @Value
    @EqualsAndHashCode(callSuper = false)
    @AllArgsConstructor(access = AccessLevel.PROTECTED)
    public static class Limit extends OrderType {
        int limitPrice;

        @Override
        public <T> T match(CaseBlock<T> caseBlock) {
            return caseBlock._case(this);
        }
    }

    public static OrderType limit(int limitPrice) {
        return new Limit(limitPrice);
    }

    public interface CaseBlock<T> {
        T _case(Market market);
        T _case(Limit limit);
    }

    public abstract <T> T match(CaseBlock<T> caseBlock);
}

OrderType.market メソッドと OrderType.limit メソッドのようにスタティックコンストラクターを用意する。その上で Market クラスと Limit クラスのコンストラクターを AllArgsConstructor アノテーションでプロテクティッドコンストラクターにしている。

テストも微修正する。

src/test/java/com/example/order/OrderTypeTest.java
package com.example.order;

import org.junit.Test;
import static org.junit.Assert.*;

public class OrderTypeTest {
    @Test
    public void testPatternMatchingOnLimitOrder() {
        OrderType orderType = OrderType.limit(100);
        int currentPrice = 100;

        boolean result = orderType.match(new OrderType.CaseBlock<>() {
            @Override
            public Boolean _case(OrderType.Market market) {
                return true;
            }

            @Override
            public Boolean _case(OrderType.Limit limit) {
                return currentPrice <= limit.getLimitPrice();
            }
        });

        assertTrue(result);
    }
}

ここまでやるとアノテーションでごちゃごちゃするのでお好みでどうぞ。

株取引の注文種別を代数的データ型として Scala で実装する

これが Scala だと簡単に実現できるということで Scala の REPL でやってみる。

scala> :paste 
// Entering paste mode (ctrl-D to finish)

sealed trait OrderType
case object Market extends OrderType
case class Limit(limitPrice: Int) extends OrderType

// Exiting paste mode, now interpreting.

defined trait OrderType
defined object Market
defined class Limit

scala> :paste 
// Entering paste mode (ctrl-D to finish)

val orderType: OrderType = Limit(100)
val currentPrice = 100

orderType match {
  case Market => true
  case Limit(limitPrice) => currentPrice <= limitPrice
}

// Exiting paste mode, now interpreting.

orderType: OrderType = Limit(100)
currentPrice: Int = 100
res3: Boolean = true

Scala だと簡潔に書ける。Kotlin も簡潔に書けそうだけど試してはいない。


今回は代数的データ型パターンなるものを説明してみた。なんだかんだ Java って表現力豊かだ。また、Java が進化する方向性として代数的データ型パターンを実装しやすくなるような文法が導入されていきそうなので、今後の Java からは目が離せなさそうである。とはいえ、Scala の表現力はやはり魅力的なので、Java も Scala も楽しんでいきたいね。

参考文献

Reactive in practice: A complete guide to event-driven systems development in Java

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javaプログラミング (クラスとインスタンス、メインメソッド)

メインメソッド

Javaは必ずクラスで構成されます。クラスのなかで処理はメソッドに書かれます。メソッドのなかで、おおもとの操作や処理はメインメソッドというメソッドに書かれます。

メインメソッドの書き方

メインメソッドは次のように書きます。クラス名は命名ルール通りにTestクラスとしましょう。

public class Test(){
  public void main(String[] args){
/*
*メインメソッドの処理の中身
*おおもとの操作はここに書かれる。
*/
 }
}

ここで

public static void main(String[] args)

という書き方は覚えてください。
ちなみに、publicとstaticの間のstaticはstatic句といい、メソッドや変数に付けられます。詳しい意味はここでは触れません。

意味があってこのようなルールになったらしいのですが、そこまで追求しなくてもいいと思います。
ただ、普通のメソッドの書き方になっていることは理解してください。

クラスを使う

では、このTestクラスのメインメソッドでCalculateクラスのmultipleIntegerNumberメソッドを使ってみましょう。
このメソッドは

public class Calculate {
 public int multipleIntegerNumber(int num){
  num = 3*num;
  return num; 
 }
}

です。

インスタンス化

クラスを使うにはまず、クラスのインスタンス化をします。

Calculate cal1 = new Calculate();

このcal1はインスタンスと呼ばれます。

メソッドの呼び出し

次にメソッドを呼び出します。

cal1.multipleIntegerNumber(3);

こうすることで、メソッドは戻り値の9を結果として返してくれます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ページ番号のロジックと参考コード(java)

■目次
1、初めに
2、ページ番号ロジック
3、コード例

■初めに
検索サイトを作った時にページ番号の表示ロジックに手間取ったので載せておこうと
思いました。参考にしていただけたら幸いです。
(一例なので改善点・よりよいコードがあれば教えてください)

■べーじ番号のロジック

ページが1の時は5までの数字を出し、間に「・・・」を表示させ最後の番号を表示させる
image.png

ページ番号が5以上の時に始めのページ・前2つのページ番号・後ろ2つ・最後の番号を表示
image.png

ページが(最後のページ ー 3)の場合は初めのページ・最後までのページと前のページを表示
image.png

■コード例(jsp)
aタグのhrefにはリンク抜き状態のコードです。
変数名や定数名などの命名はセンスがないので良い名前があれば教えてください。

<%
//ページ数・現在ページの変数宣言
int currentPage;
int lastPageNum;
//変数代入
currentPage = 5;
lastPageNum = 9;
//定数宣言
final int FIRST_PAGE = 1;
final int FIRST_HALF_CHECK = 5;
final int LAST_HALF_CHECK = 4;
final int BEFORE_AND_AFTER = 2;
%>

<section>
<div class="pageLeft">
<%if(currentPage != FIRST_PAGE){ %>
<a href="">前のページへ</a>
<%} %>
</div>
<div class="pageCenter">
<%if(currentPage >= FIRST_HALF_CHECK){ %>
<a href="">1</a>
…
<%} %>
<%if(currentPage < FIRST_HALF_CHECK){ %>
    <%for(int i = 1; i <= FIRST_HALF_CHECK; i++){ %>
        <%if(i <= lastPageNum){ %>
        //aタグを入れるか入れないかの分岐(現在ページにaタグは入れない)
            <%if(i == currentPage){ %>
                <span><%=i %></span>
            <%}else{ %>
                <a href=""><%=i %></a>
            <%} %>
        <%} %>
    <%} %>
<%}else if(lastPageNum - currentPage < LAST_HALF_CHECK){ %>
    <%for(int i = lastPageNum - LAST_HALF_CHECK; i <= lastPageNum; i++){ %>
        <%if(i > 0){ %>
        //aタグを入れるか入れないかの分岐(現在ページにaタグは入れない)
            <%if(i == currentPage){ %>
                <span><%=i %></span>
            <%}else{ %>
                <a href=""><%=i %></a>
            <%} %>
        <%} %>
    <%} %>
<%}else{ %>
    <%for(int i = currentPage - BEFORE_AND_AFTER; i <= currentPage + BEFORE_AND_AFTER; i++){ %>
        //aタグを入れるか入れないかの分岐(現在ページにaタグは入れない)
        <%if(i == currentPage){ %>
            <span><%=i %></span>
        <%}else{ %>
            <a href=""><%=i %></a>
        <%} %>
    <%} %>
<%} %>
<%if(lastPageNum - currentPage >= LAST_HALF_CHECK){ %>
    …<a href=""><%=lastPageNum %></a>
<%} %>
</div>
<div class="pageRight">
<%if(currentPage != lastPageNum){ %>
    <a href="">次のページへ</a>
<%} %>
</div>
</section>

■まとめ
検索サイトでページナンバーロジックに1回詰まったので書いてみました。
もっといい方法や改善点等あると思いますので、その際は教えていただけると幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DefaultGroovyMethodsとStream APIの相互変換まとめ

GroovyのDefaultGroovyMethodsとJavaのStream APIとの相互変換

仕事でGroovyのDefaultGroovyMethodsをJavaのStream APIを使った形に書き直すという作業を行ったので、備忘録も兼ねてまとめます。

サンプルコードの動作はそれぞれ以下のバージョンで確認しています。
Java: 11
Groovy: 2.5.8

DefaultGroovyMethod ⇔ Stream API

each ⇔ forEach

Groovy
def list = ["Java", "Groovy", "Scala"]
list.each { println it }

---結果---
Java
Groovy
Scala
Java
List<String> list = List.of("Java", "Groovy", "Scala");
list.forEach(System.out::println);

---結果---
Java
Groovy
Scala

collect ⇔ map + collect

Groovy
def lowerCases = ["java", "groovy", "scala"]
def upperCases = lowerCases.collect { it.toUpperCase() } 
println upperCases

---結果---
[JAVA, GROOVY, SCALA]
Java
List<String> lowerCases = List.of("java", "groovy", "scala");
List<String> upperCases = lowerCases.stream().map(language -> language.toUpperCase()).collect(Collectors.toList());
System.out.println(upperCases);

---結果---
[JAVA, GROOVY, SCALA]

findAll ⇔ filter + collect

Groovy
def numbers = [1, 2, 3, 4, 5] 
def odds = numbers.findAll { it % 2 != 0 } 
println odds

---結果---
[1, 3, 5]
Java
List<Integer> numbers = List.of(1, 2, 3, 4, 5);
List<Integer> odds = numbers.stream().filter(number -> number % 2 != 0).collect(Collectors.toList());
System.out.println(odds);

---結果---
[1, 3, 5]

参考資料

公式リファレンス

List
https://docs.oracle.com/javase/jp/8/docs/api/java/util/List.html
Stream
https://docs.oracle.com/javase/jp/8/docs/api/java/util/stream/Stream.html
Collectors
https://docs.oracle.com/javase/jp/8/docs/api/java/util/stream/Collectors.html

DefaultGroovyMethods
http://docs.groovy-lang.org/latest/html/api/org/codehaus/groovy/runtime/DefaultGroovyMethods.html

Qiita記事

Groovyよく使いそうなメソッド(List編)
https://qiita.com/kimromi/items/e326bf9c24220df97ecf

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GroovyのDefaultGroovyMethodsとJavaのStream APIの相互変換まとめ

仕事でGroovyのDefaultGroovyMethodsをJavaのStream APIを使った形に書き直すという作業を行ったので、備忘録も兼ねてまとめます。

サンプルコードの動作はそれぞれ以下のバージョンで確認しています。
Java: 11
Groovy: 2.5.8

DefaultGroovyMethod ⇔ Stream API

each ⇔ forEach

Groovy
def list = ["Java", "Groovy", "Scala"]
list.each { println it }

---結果---
Java
Groovy
Scala
Java
List<String> list = List.of("Java", "Groovy", "Scala");
list.forEach(System.out::println);

---結果---
Java
Groovy
Scala

collect ⇔ map + collect

Groovy
def lowerCases = ["java", "groovy", "scala"]
def upperCases = lowerCases.collect { it.toUpperCase() } 
println upperCases

---結果---
[JAVA, GROOVY, SCALA]
Java
List<String> lowerCases = List.of("java", "groovy", "scala");
List<String> upperCases = lowerCases.stream().map(language -> language.toUpperCase()).collect(Collectors.toList());
System.out.println(upperCases);

---結果---
[JAVA, GROOVY, SCALA]

findAll ⇔ filter + collect

Groovy
def numbers = [1, 2, 3, 4, 5] 
def odds = numbers.findAll { it % 2 != 0 } 
println odds

---結果---
[1, 3, 5]
Java
List<Integer> numbers = List.of(1, 2, 3, 4, 5);
List<Integer> odds = numbers.stream().filter(number -> number % 2 != 0).collect(Collectors.toList());
System.out.println(odds);

---結果---
[1, 3, 5]

参考資料

公式リファレンス

List
https://docs.oracle.com/javase/jp/8/docs/api/java/util/List.html
Stream
https://docs.oracle.com/javase/jp/8/docs/api/java/util/stream/Stream.html
Collectors
https://docs.oracle.com/javase/jp/8/docs/api/java/util/stream/Collectors.html

DefaultGroovyMethods
http://docs.groovy-lang.org/latest/html/api/org/codehaus/groovy/runtime/DefaultGroovyMethods.html

Qiita記事

Groovyよく使いそうなメソッド(List編)
https://qiita.com/kimromi/items/e326bf9c24220df97ecf

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Caused by: java.lang.NoSuchMethodError: 'boolean javax.servlet.ServletContext.setInitParameter(java.lang.String, java.lang.String)'

log
Caused by: java.lang.NoSuchMethodError: 'boolean javax.servlet.ServletContext.setInitParameter(java.lang.String, java.lang.String)'

Cause

The error indicates multiple version servlet-api jar in class path.

  • javax.servlet:servlet-api (< 3.0)
  • javax.servlet:javax.servlet-api (>= 3.0)

Resolution

Remove old servlet-api dependency.
Basically, old class depends on old servlet api will work with new api.

build.gradle
configurations.all {
    exclude module: "servlet-api" // Old pre-3.0 servlet API artifact
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Selenium × Java

SeleniumをJavaで使ってみた

Seleniumとは

※以下抜粋
Seleniumは、Webアプリケーションをテストするためのポータブルフレームワークです。
Seleniumは、テストスクリプト言語を学ぶ必要なく機能テストを作成するための再生ツールを提供します。

要はwebアプリケーションを自動で実行できたり、HTMLを解析(Webスクレイピング)するためのライブラリ。python、node.js、Java、PHPなど多言語でライブラリが用意されており、簡単に実装が可能。

経緯

会社の勤務システムがwebアプリケーションで動いており、PC起動時にブラウザ起動から提出まで自動化したいというのが最終目標。本記事はSelenium導入、「Chrome起動→Google検索を開く」までとする。

アーキテクト

言語 Java( ver.11 )
build Maven
ブラウザ Google Chrome
IDE IntelliJ

手順

前提
java x Mavenのアプリケーションの環境構築済みであること。

準備

1. Seleniumのライブラリをpomに依存ライブラリを追加、読み込み。

<dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-java</artifactId>
    <version>3.12.0</version>
</dependency>

2. Chromeで実行するため、Chromeのdriverをダウンロードする。これにより、Chromeによる実行が可能となる。別ブラウザでやりたい場合は、各ブラウザのdriverをダウンロードすればOK。

chromedriverのダウンロード
http://chromedriver.chromium.org/downloads

サンプルコードの実行

「Chrome起動→Google検索を開く」

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;

public class MainApplication {

    public static void main(String[] args){
        final String PATH = "※chromeDriverのpath";
        System.setProperty("webdriver.chrome.driver", PATH);
        WebDriver driver = new ChromeDriver();

        final String URL = "http://www.google.com";
        // URL遷移
        driver.get(URL);
    }
}

アプケーションを実行して、以下のようにGoogle検索が表示されれば成功:clap:
スクリーンショット 2019-11-24 10.48.38.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JJUG CCC 2019 Fall 資料まとめました

タイトルのとおり昨日開催された「JJUG CCC 2019 Fall」の資料をまとめました。

資料が見つからなかった場合のリンクは、部屋番号のハッシュタグといった関連する内容のツイッターになっています。

ーーー

タイムテーブル

10:00〜10:45

A+B
試して学ぼう、Java EE アプリケーションをOpenShift(Minishift)でデプロイ!

C+D
Head toward Java 13 and Java 14

E+F
All people are VIP~Disney哲学から考えるDiversity

G+H
Gradle を完全に理解した人が、何も分からなくなるための第一歩

I
JavaとGraalVMとPicocliで、ときめくネイティブコマンドラインアプリを作ろう

L
Coding That Sparks Joy with Quarkus

M前半
BigQueryを用いたデータ分析基盤作成入門

M後半
バーコードリーダから飛ばされたJANコードを元にJavaからのAPI呼び出しで商品情報を取得してFirebaseに登録&ロケットチャットに通知した話

11:00〜11:45

A+B
Javaで学ぶオブジェクト指向プログラミングの基礎知識

C+D
入門 例外

E+F
Rethinking Runtime Efficiency with GraalVM

G+H
Javaプログラマのための頑張らないGo入門

I
Jakarta EE: today and tomorrow

L
Gradle Ex Machina

M
Spring Cloud AWSをクラウドエンジニアがいじってみた

12:00〜12:45

C+D
JJUG初心者のためのJavaコミュニティのススメ

E+F
Java によるクラウドネイティブ の実現に向けて

13:30〜14:15

A+B
最新:Azure Spring Cloud のご紹介

C+D
Reliability Engineering Behind The Most Trusted Kafka Platform at LINE

E+F
開け!ドメイン駆動設計の扉

G+H
Mavenの真実とウソ

I
Javaで学ぶネットワークプログラミングの基礎

L
Modern Identity Management (in the Era of Serverless and Microservices)

M
運用を支えるためのログを出すにはどうするか?

14:30〜15:15

A+B
Spring Update 2019

C+D
CLRのValueTypeを起点にProject Valhallaを覗いてみる

E+F
長く続くサービスがモダンであり続けるには

G+H
まだまだ間に合うJUnit(再)入門

I
Micronaut と始めるサーバーサイドKotlin

M
JavaオンプレシステムをAKS + Quarkusに移行した話

N
元インフラエンジニアがSpringBoot2を用いてFW開発を学んでいる話

15:45〜16:30

A+B前半
Java における乱数 (生成器) とのつき合い方

A+B後半
Use Kotlin scripts and custom DSL in your web apps

C+D
こわくないソースコードリーディング生活

E+F
AngularとSpring Bootで作るSPA + RESTful Web Serviceアプリケーション - 開発ツールやプロジェクト構成も含めて45分で丸わかり -

G+H
Javaの起動速度といかに戦うか

I
How to adapt MicroProfile API for general Web applications

M
Where is my cache? Architectural patterns for caching microservices by example

16:45〜17:30

A+B前半
新卒3年目が立ち向かった、お名前.comでの超巨大レガシーシステム脱却の事例

A+B後半
オレ流OpenJDK「の」開発環境

C+D
JavaでTracing

E+F
JVM入門 -Javaプログラムが動く仕組み-

G+H
Serverless時代のJavaについて

I
多言語対応の仮想マシンGraalVMが照らす未来

M
Evaluating ZGC with HBase

17:45〜18:30

A+B
分散トレーシングの技術選定・OSS 貢献と Stackdriver Tracer での性能可視化・改善

C+D
Swagger ではない OpenAPI Specification 3.0 による API サーバー開発

G+H
DIコンテナ入門

I
JVMs in Containers: Best Practices

M
Oops-Less Operation

ーーー

JJUGスタッフ/発表者/スポンサーの皆様ありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SpringBoot×OpenAPI入門 〜Generation gapパターンで作るOpenAPI〜

背景

Javaのコミュニティイベント「JJUG CCC 2019 Fall」に参加。
そこで聞いた「Swagger ではない OpenAPI Specification 3.0 による API サーバー開発」の登壇内容に触発されて超簡単なOpenAPIを作った話。(浅いです)

今回のゴール

SpringBootを使ってOpenAPIを書く

OpenAPIを使った開発フロー(ざっくり)

image.png

ルール:Generation gapパターン → 自動生成したクラスファイルは編集しない。

1. API定義ファイルを用意

OAS(OpenAPISpecification)にあるサンプルを使用

https://github.com/OAI/OpenAPI-Specification/blob/master/examples/v3.0/petstore.yaml

2. 定義よりコード自動生成

Gradleのプラグイン【openapi-generator-gradle-plugin】を使用。
APIの定義ファイルよりコードを自動生成してくれるGradleプラグイン。

build.gradle
plugins {
    id "org.openapi.generator" version "4.2.1"
}
ポイント1:コンパイル時の設定

自動生成したクラスファイルをコンパイル時に含めるように設定

build.gradle
compileJava.dependsOn tasks.openApiGenerate
sourceSets.main.java.srcDir "${openApiGenerate.outputDir.get()}/src/main/java"
sourceSets.main.resources.srcDir 
ポイント2:OpenapiGeneratorの設定

openapi-generator-gradle-pluginの実行例をほぼほぼコピーでOK。
ただ、インターフェイスを作成するようにコンフィグファイルを操作。

build.gradle
//https://github.com/OpenAPITools/openapi-generator/tree/master/modules/openapi-generator-gradle-plugin

openApiGenerate {
    generatorName = "spring"
    //コンフィグ設定
    configFile = "$rootDir/specs/config.json".toString()
    //サンプルAPI指定
    inputSpec = "$rootDir/specs/petstore-v3.0.yaml".toString()
    outputDir = "$buildDir/generated".toString()
    apiPackage = "org.openapi.example.api"
    invokerPackage = "org.openapi.example.invoker"
    modelPackage = "org.openapi.example.model"
    configOptions = [
        dateLibrary: "java8"
    ]
    systemProperties = [
        modelDocs: 'false'
    ]
}
config.json
{
  "interfaceOnly": true
}

クラスの自動生成はGradleのタスク【openApiGenerate】でOK。

スクリーンショット 2019-11-24 9.40.36.png

タスク実行すると、以下のJavaファイルが自動生成される

image.png

3. API実装

自動生成されたインターフェイスからimplementして、コントローラーを生成

PetsApiController.java
@RestController
public class PetsApiController implements PetsApi{
    @Override
    public ResponseEntity<List<Pet>> listPets(@Valid Integer limit) {
        System.out.println("Here list pet");
        return new ResponseEntity<>(HttpStatus.OK);
    }
}

完成!!

完成したコード

https://github.com/harutotanabe09/SpringBootOpenAPIBegin

おまけ:OpenAPIのドキュメントツールを使ってみた

OpenAPIのドキュメントツール(redoc)を使って、ドキュメントを自動生成。
※ 前提:Dockerが入っていること

docker run -it --rm -p 80:80 \
  -v $(pwd)/specs/petstore-v3.0.yaml:/usr/share/nginx/html/swagger.yaml \
  redocly/redoc

http://localhost/

出力イメージ:定義ファイルを元にAPIのパラメータや戻り値を見やすい画面で出力

image.png

参考資料

Swagger ではない OpenAPI Specification 3.0 による API サーバー開発
https://www.slideshare.net/techblogyahoo/swagger-openapi-specification-30-api

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Gradle5 で Lombok が使えない

macOS x IDEA x Gradle5 x Spring-boot2 で Lombok を使おうとして、コンパイルはできるがビルドができない。

アノテーションの設定や Lombok Plugin の設定は済んでいて、@Slf4 を記述し、log.debug を記載してエディタ上でエラーは発生しないけど、ビルドすると以下エラーが発生する。

/src/to/path/TestController.java:14: エラー: シンボルを見つけられません
        log.info("start");
        ^
  シンボル:   変数 log
  場所: クラス TestController

以下の設定を build.gradle に追加したら解決

compileOnly group: 'org.projectlombok', name: 'lombok', version: "${lombokVersion}"
annotationProcessor group: 'org.projectlombok', name: 'lombok', version: "${lombokVersion}"

※「providedCompile」で読み込もうとしていたのが誤りのようでした。

参考:
https://intellij-support.jetbrains.com/hc/en-us/community/posts/360003400520-Intellij-Idea-Lombok-Gradle-5-broken-build

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

IntelliJでJarファイルをワンクリックでpluginにビルドする

マイクラでPluginの開発をしている時に、すこしでも時間短縮をしたかったので、ワンクリックで使用したいサーバーのpluginsへぶっこむ方法を書きます。

環境

  • Windows 10 Pro
  • IntelliJ IDEA 2019.2.2

素晴らしい前置き

今日もいい天気なのに、どうして僕(私)はいちいちビルドしてからビルドしたjarファイルをいちいちpluginsファイルに置いてreloadしないといけないんだろう...と思ったことはありませんか?かなりの開発者が思ったことがあるはずです。

さて、そんなあなたに朗報です。右上の緑の右三角を押すだけで、そのプロセスをすべて行って、コンソールでreloadとうつだけであなたの最高で素晴らしいプラグインが試せます!では参りましょう。

Build Artifactを作る

基本設定

1... Fileを押す
2... Project Structureを押す
3... Project Settingsを見る
4... Artifactsを押す
intellij_1.png
intellij_2.png

5... 上のプラスボタンを押す
6... JAR ▶を押す
7... From modules with dependenciesを押す
8... 画像と特に違いがなければ、OKを押す1

intellij_3.png
intellij_4.png
intellij_5.png

9... Output Layoutの下辺りにあるプラスボタンを押す
10... Fileを押す
11... プロジェクトへのパス\src\main\resources\plugin.ymlを指定する
12... OKを押す
13... Output Directoryに、サーバーのpluginフォルダを設定する
14... OKを押す

intellij_5.png
intellij_6.png
intellij_7.png

Build Artifactを試してみる

1... 上のメニューのBuildを押す
2... Build Artifactsを押す
3... 先程作ったArtifactを押す
4... Buildを押す

intellij_8.png
intellij_9.png

これで、pluginsファイルにプラグインの名前のjarファイルが生成されているはずです!やったあ!
あとは、サーバーで実行してテストしてみてください。

ボタンを押して指定したディレクトリへビルドする

1... 右上のSample Buildを押す
2... Edit Configurationsを押す
3... 左上のプラスボタンを押す
4... JAR Applicationを押す
5-1... Nameを変える(任意)
5-2... Path to JARを、サーバーのjarのパスにする
5-3... Working Directoryを、サーバーのルート(一番浅いフォルダ)にする
5-4... JREをDefaultにする1
5-5... Search sources using module's classpathをにする
6... 先ほど作ったArtifactを設定する(チェックを付ける)
7... OKを押す

intellij_10.png
intellij_11.png

あとは、右上のドロップダウンからいま作ったJAR Applicationを選択して、Runボタンを押せばサーバーが起動してビルドしたものが自動で読み込まれているはずです!

それでは皆様、よき開発ライフを。

参考

https://www.spigotmc.org/threads/tip-one-click-artifact-build-and-spigot-server-start-in-intellij.308427/


  1. 場合によっては設定が変わることもあるかもしれませんので、環境によって合わせてください 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javaの入門書を一通り読んだので何か作ってみる

javaの入門書を一通り読んだので...

プログラミングは作ってなんぼ!!!
ということで何か簡単なゲームでも作ろうかと思い考えたのはずばり『じゃんけん!!!』(しょぼい)
これは自分のプログラミングロード第一歩ということでゲームの原点といってもいい『じゃんけん』!!!でちょうどいいじゃんと思い即座にコードを書いてみました!!!!!


class Janken{
    public static void main(String[] args){
        janken();
    }
    //メイン


    public static void janken(){
        int loop = 0;
        //あいこの場合loop==0、勝利か負けの場合loop=1,
        //※loop変数であいこの場合でもじゃんけんを繰り返す


        while(loop==0){
        String hand[] = {"グー","チョキ","パー"};
        //出し手変数
        System.out.println("最初はグー!じゃんけん・・・");
        System.out.println("グー、チョキ、パー:0,1,2");
        int myHand = new java.util.Scanner(System.in).nextInt();
        //自分の出し手

        int youHand = new java.util.Random().nextInt(3);
        //相手の出し手

        System.out.println("自分:"+hand[myHand]+",相手:"+hand[youHand]);
        if(myHand == youHand){
            System.out.println("あいこです( ゚Д゚)");
        }
        else if((myHand==0 && youHand==1) || (myHand==1 && youHand==2) || (myHand==2 && youHand==0)){
            System.out.println("勝利です)^o^(");
            loop += 1;
        }
        else{
            System.out.println("負けです( ;∀;)");
            loop += 1;
            }

    }
    }
    //じゃんけんメソッド
}

まだまだ稚拙だけどこれから頑張ります!!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javaにおけるスレッド実装方法の比較とラムダ式的記述方法

はじめに

 急遽、Javaでマルチスレッドプログラムを作れという職場からの
 無茶苦茶な要望に答えるべく速攻で勉強したことをまとめておく。
 ※ Javaの経験日数はまだ3日程度なのでミスしてたらご指摘ください。

結論:とりあえずサクッと起動したい場合

サブスレッド起動までのパスが少ない&サブスレッドをインスタンスとして保持しないなら、ラムダ式を組み合わせて

ThreadStartTemplate.java
new Thread( () -> {サブスレッドで実行したいメソッド or 処理} ).start()

でよさそう。便利っすねぇー。
※僕の中では、ラムダ式はローカルな無名クラスと理解しています。

実行環境

自前でEclipseのインストールとかは後回しにしてとりあえず、文法を確かめるためにpaiza.ioを使いました。

サブスレッドの実装方法

以下の2パターンでスレッドを実装できるみたい。
 1. Rnnableインタフェースを使用する。
 2. Threadクラスを継承する。

1. Runnableインタフェースを使う

JavaにはRunnableインタフェースなるものがあるらしく、run()をオーバーライドすることで、スレッド時に実行される処理が実装できるようだ。
使い方は以下の通り。
1. Runnableインタフェースを実装したクラスのインスタンスを生成
2. コンストラクタにRunnnableオブジェクトを渡して、Thread型のオブジェクトを生成
3. Threadクラスのstart()メソッドをコールしてサブスレッドを起動

この通りに従って書いたサンプルコードがRunnableSample.javaです。

RunnableSample.java
import java.util.*;

public class Main {

    private static class RunnableSample implements Runnable{

        private String thread_no;
        RunnableSample(String str){
            this.thread_no = str;
        }
        public void run(){
            System.out.println("RunnableThread Starting No."+thread_no);
        }
    }

    public static void main(String[] args) throws Exception {
        String str1 = "1";
        String str2 = "2";
        RunnableSample runner1 = new RunnableSample(str1); // 1.
        RunnableSample runner2 = new RunnableSample(str2); // 1.
        Thread thread1 = new Thread(runner1); // 2.
        Thread thread2 = new Thread(runner2); // 2.
        thread1.start(); // 3.
        thread2.start(); // 3.

        Thread.sleep(200);
        System.out.println("MainThread End");
    }
}
実行結果
RunnableThread Starting No.1
RunnableThread Starting No.2
MainThread End

MainクラスのローカルクラスとしてRunnableSampleクラスを定義しています。
MainスレッドからRunnableSampleクラスのオブジェクトを生成し、2つのサブスレッドとして実行します。

RunnableThread Starting No.1
RunnableThread Starting No.2
は逆順で出力される可能性がある。200msのスリープを挟んでいるのでMainThread Endはたいてい最後に出力されるでしょう。

2. Threadクラスを継承する。

次にThreadクラスを継承してサブスレッドの処理を実装する方法について書く。
使い方は次の通り。コードはThreadSample.java。

  1. Threadクラスのrun()をオーバーライドしたクラスのインスタンスを生成
  2. Threadクラスのstart()メソッドをコールしてサブスレッドを起動
ThreadSample.java
import java.util.*;

public class Main {

    private static class ThreadSample extends Thread{

        private String thread_no;
        ThreadSample(String str){
            this.thread_no = str;
        }
        public void run(){
            System.out.println("RunnableThread Starting No."+thread_no);
        }
    }

    public static void main(String[] args) throws Exception {

        String str1 = "1";
        String str2 = "2";
        ThreadSample thread1 = new ThreadSample(str1); // 1.
        ThreadSample thread2 = new ThreadSample(str2); // 1.
        thread1.start(); // 2.
        thread2.start(); // 2.

        Thread.sleep(200);
        System.out.println("MainThread End");
    }
}

実行結果
RunnableThread Starting No.2
RunnableThread Starting No.1
MainThread End

実行結果は同じになる(今回はThread1とThread2の出力の順番が逆転した)。

結局「Runnableインタフェースの実装」と「Threadクラスの継承」どちらがいいのか?

基本的にはRunnableインタフェースを使った方がメリットが多そう。

  • Javaは多重継承を許していないため、Threadクラスを継承すると他のクラスが継承できなくなってしまう。Runnableインタフェースを実装したクラスであれば他のクラスを継承することができる。
  • Threadクラスが抱えるオーバーヘッドをそのまま受け継がなくてすむ。
  • Threadクラスを継承した方がコードはスッキリしそう。

Runnableインタフェースでもすっきりと書けたら完璧やん。ということでやってみる。

Runnableインタフェースをラムダ式で記述を簡単にする。

Java8のラムダ式を理解するを参考にラムダ式でコードを簡潔にしてみる。

LambdaThreadSample.java
import java.util.*;

public class Main {

    static private void subthread_run(String thread_no){
        System.out.println("RunnableThread Starting No."+thread_no);
    }

    public static void main(String[] args) throws Exception {

        String str1 = "1";
        String str2 = "2";
        new Thread( () -> {subthread_run(str1);} ).start();
        new Thread( () -> {subthread_run(str2);} ).start();

        Thread.sleep(200);
        System.out.println("MainThread End");
    }
}
実行結果
RunnableThread Starting No.2
RunnableThread Starting No.1
MainThread End

詳しい記述方法はJava8のラムダ式を理解するを参考にしていただくとするが、難解に簡単に説明すると、先のRunnableSampleクラスをsubthread_runメソッドをもつ無名クラスとし、Threadクラスのオブジェクトをnewしてstartしている。
ラムダ式を用いることで随分とプログラムがすっきりとし可読性もぐっと上がった。Threadをインスタンスとして管理する必要がないならこの書き方が良さそうだ。次はSynchronizedについてまとめたい。

参考文献

世界で戦うプログラミング力を鍛える150問 Chapter.16:スレッドとロック

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む