20191127のJavaに関する記事は16件です。

再帰処理を使ったファイルの削除【Java】

はじめに

本記事では再帰処理を用いてファイルやディレクトリを削除する方法を紹介します。
初投稿なのでご意見等あればコメントお願いします!

実装

/**
 * ファイルを再帰的に削除する
 * @param file 削除するファイルまたはディレクトリ
 */
public static void deleteFile(File file) {

    // ファイルが存在しなければ終了
    if (!file.exists()) {
        return;
    }   
    // ディレクトリであれば子を削除
    if (file.isDirectory()) {
        for (File child : file.listFiles()) {
            deleteFile(child);
        }
    }       
    // 対象のファイルを削除
    file.delete();
}

ディレクトリであれば子のファイルオブジェクトを先に削除することで空の状態で削除することができます。

おまけ

再帰処理が使えて喜んだのも束の間、やっぱり先駆者様がたくさんいました…笑
これだけで終わるのはもったいないので、少し応用的なものを紹介します!

/**
 * ファイルまたはディレクトリに含まれるファイルの数のみをカウントする
 * @param file ファイルまたはディレクトリ
 * @return ファイルの数
 */
public static int countFile(File file) {                
    int count = 0;  
    // ファイルが存在しなければ終了
    if (!file.exists()) {
        return count;
    }
    // ディレクトリであれば子を再帰的にカウント, ファイルであれば自己をカウント
    if (file.isDirectory()) {
        for (File child : file.listFiles()) {
            count += countFile(child);
        }
    } else {
        count = 1;
    }
    // 合計を返却
    return count;
}

上のメソッドは引数のオブジェクト以下に存在するファイルの数のみをカウントできます!
このメソッドの実用性はともかく、再帰処理はプログラミングしてる感じがして好きですね笑

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

Java学習5日目

スッキリわかるjava入門 第2版 p250まで

5章のまとめの練習問題で正しいメソッドの作成方法を学んだ。戻り値(return)がある場合はvoidではなく戻り値の型にする事 returnの前に返す値をメソッド内で定義してから戻り値に返すようにする

JDKを用いた開発の全体像

①テキストエディタでソースコードを作成
②javacコマンドでコンパイルしクラスファイル(機械語に変換)
③javaコマンドで実行され、黒い画面(コマンドプロンプト)に実行結果が出力される


Javaプログラムの完成品は、複数クラスファイルの集合体なので、一つのクラスファイルで作成されている訳ではない。なので誰かに配布する場合は、全てのクラスファイルを渡す必要がある

別のクラスからメソッドを呼び出す場合は、クラス名.メソッド名()で呼ぶ
パッケージには親子関係や階層関係はない、package文がないクラスにはデフォルトパッケージが指定されてimport文でインポートすることはできない。別のパッケージからメソッドを呼び出す場合は「所属パッケージ名.クラス名.メソッド名」と書く = この事をFQCN(完全限定クラス名)という
FQCNの入力を省略したい場合はimport文を追加する。基本的にJavaでは一切の宣言をすることなくJVMが扱える全てのクラスを常時使うことが出来る

API(便利な機能)をimportして使うことによって、時間削減して大きなプログラムを作ることが可能。APIリファレンスをサイトで参照できる

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

手元にあるjarファイルがどのバージョンのjavaで利用できるのかを調べたい

手元にあるjarファイルがどのバージョンのjavaで利用できるのかを調べるにはどうすればよいでしょうか?

実をいうと、jarファイルそのものはビルドのバージョンを有していません。jarファイルというのはclassファイルやpropertiesファイルやそのほかファイルをアーカイブしたものにすぎません。たとえるなら、jarはtarやzipの親戚とでもいえばよいでしょうか。したがって、Javaの特定のバージョンでそのjarファイルが利用可能かどうかを調べるには、jarファイルそのもののバージョンではなく、そのjarファイルに格納されているclassファイルがどのバージョンのjavacでビルドされたのかを見てやる必要があります。

ここでは調査方法の例としてcommons-collections4-4.4.jar (Apache Commons Collections v.4.4) がどのバージョンから利用できるのかを調査してみます。

まずjarコマンドでjarファイルの中身を取り出します。

$ jar xf commons-collections4-4.4.jar

するとjarファイルに格納されていたclassファイルが複数見つかるはずです。ここではサンプルとして、./org/apache/commons/collections4/ArrayStack.classを利用することにします。

$ find . -name "*.class"  | head -n1
./org/apache/commons/collections4/ArrayStack.class

./org/apache/commons/collections4/ArrayStack.classがどのバージョンのjavacによりビルドされたのかを調べたいわけですが、まず手っ取り早いのがfileコマンドを利用する方法です。この場合はJava8でビルドされていることが一目瞭然ですね。

$ file ./org/apache/commons/collections4/ArrayStack.class
./org/apache/commons/collections4/ArrayStack.class: compiled Java class data, version 52.0 (Java 1.8)

fileコマンドがない環境ではjavapを利用しましょう。

$ javap -v ./org/apache/commons/collections4/ArrayStack.class | grep major
  major version: 52

javapはclassファイルを逆アセンブルするコマンドで、-vオプションを付けると詳細情報が出力され、その中にバージョン情報が混ざっています。これをgrepでより分けているわけですね。ここで表示されているmajor version: 52という値はJava8を指しています。このmajor versionとJavaのバージョンの対応関係については、次のページがよくまとまっているので、参考にしてください → Wikipedia: Java class file

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

自主勉0日目

プログラミングの学習が開いてしまい、すっかり忘れてしまうのが嫌なので、自主勉することにしました。
今日は風邪で寝ながらなので、とりあえずアカウントだけ作ってみた。

わたしのjavaの先生は最高に面白い、ダイハードな先生だ。
コードがスラスラ書ける気にさせてくれる、という面でも先生としてすごいと思う。

いくつかプログラミング言語の記事を読んでみたが、短期間で、オブジェクト指向を理解させてくれたんだなと実感した。
一番だいじなことを、限られた時間の中で、最優先で理解させてくれた先生の教え。
でも、離れていると忘れてしまう!
なんてもったいない!

というわけで、できるだけ毎日、なにかしら勉強して、記憶を維持していきたいと思う。

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

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

クラスメソッド

ここではクラスメソッドについて紹介します。
クラスのなかの変数でstaticが宣言時に頭についたものをクラス変数と言いました。同じように、メソッドでstaticが宣言時に頭についたものをクラスメソッドと言います。クラスの構成やメソッドの書き方や呼び出し方がわかれば読めると思います。また、アクセス指定子も説明は省きます。(知らなくても読めると思いますが。)

クラスメソッドの特徴

クラスメソッドは呼び出すときにはインスタンス化をしません。呼び出すときはクラス名を使います。それによって便利なことや注意するべきことがあります。

クラスメソッドの構成

クラスメソッドの定義の仕方

クラスメソッドの書き方は次のようになります。

public class クラス名{ 
  public static 戻り値の型 メソッド名(引数の型 引数){
  // メソッドの中身
  }
}

staticがつくだけです。なので、構成というほどのものはありません。

クラスメソッドの呼び出し方

クラスメソッドはインスタンスせずにクラス名を使って

クラス名.メソッド名(引数の型 引数);

みたいに呼び出します。

クラスメソッドの例

次にクラスメソッドの例を書いてみましょう。
クラス名はCallクラスです。

public void Call(){
  public static void Dog(){
   System.out.println("ワンワン");
  }

  public static void Cat(){
    System.out.println("ニャーニャー");
  }

  public static void Monkey(){
    System.out.println("ウキー");
  }
}

このクラスのstaticなメソッドをメインメソッドから呼び出します。このとき、インスタンス化せずにクラス名を使って呼び出していることに注意してください。

public class MainMethod(){
 public static void main(String[] args){
  Call.Dog();
  Call.Cat();
  Call.Monkey();
 }
}

これで次のように

ワンワン
ニャーニャー
ウキー

という出力になります。

クラスメソッドの注意

クラスメソッドはインスタンス化しません。つまり、インスタンス毎に区別することが出来ません。例えばつぎのクラスでははインスタンス毎のメソッドが呼び出された回数を変数instans_countでカウントしています。この変数はインスタンス化するからこそ意味があります。なので、staticなメソッドでは意味をなさなくなります。

public class ClassValue2 {
    private int s = 3;
    private static int static_count = 0;
    private int instans_count = 0;

    public void add(int num){
        s = num + s;
        static_count = static_count + 1;
        instans_count = instans_count + 1;
    }

    public void substract(int num){
        s = -num + s;
        static_count = static_count + 1;
        instans_count = instans_count + 1;
    }

    public void multiple(int num){
        s = num*s;
        static_count = static_count + 1;
        instans_count = instans_count + 1;
    }
    public void division(int num){
        s = 1/num*s;
        static_count = static_count + 1;
        instans_count = instans_count + 1;
    }

    public void confirmation(){
        System.out.println("static_count:" + static_count);
        System.out.println("instans_count:" + instans_count);
        System.out.println("s:" + s);
    }

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

Javaプログラミング (クラスメソッド)

クラスメソッド

ここではクラスメソッドについて紹介します。
クラスのなかの変数でstaticが宣言時に頭についたものをクラス変数と言いました。同じように、メソッドでstaticが宣言時に頭についたものをクラスメソッドと言います。クラスの構成やメソッドの書き方や呼び出し方がわかれば読めると思います。また、アクセス指定子も説明は省きます。(知らなくても読めると思いますが。)

クラスメソッドの特徴

クラスメソッドは呼び出すときにはインスタンス化をしません。呼び出すときはクラス名を使います。それによって便利なことや注意するべきことがあります。

クラスメソッドの構成

クラスメソッドの書き方と使い方をまとめました。

クラスメソッドの定義の仕方

クラスメソッドの書き方は次のようになります。

public class クラス名{ 
  public static 戻り値の型 メソッド名(引数の型 引数){
  // メソッドの中身
  }
}

staticがつくだけです。なので、構成というほどのものはありません。
ただし、staticなメソッドはstaticなメソッドしか使えません。

クラスメソッドの呼び出し方

クラスメソッドはインスタンスせずにクラス名を使って

クラス名.メソッド名(引数の型 引数);

みたいに呼び出します。

クラスメソッドの例

次にクラスメソッドの例を書いてみましょう。
クラス名はCallクラスです。

public void Call(){
  public static void Dog(){
   System.out.println("ワンワン");
  }

  public static void Cat(){
    System.out.println("ニャーニャー");
  }

  public static void Monkey(){
    System.out.println("ウキー");
  }
}

このクラスのstaticなメソッドをメインメソッドから呼び出します。このとき、インスタンス化せずにクラス名を使って呼び出していることに注意してください。

public class MainMethod(){
 public static void main(String[] args){
  Call.Dog();
  Call.Cat();
  Call.Monkey();
 }
}

これで次のように

ワンワン
ニャーニャー
ウキー

という出力になります。

クラスメソッドの注意

クラスメソッドはインスタンス化しません。つまり、インスタンス毎に区別することが出来ません。例えばつぎのクラスではインスタンス毎のクラスの中のメソッドが呼び出された回数を変数instans_countでカウントします。この変数instans_countはインスタンス化するからこそ意味があります。なので、staticなメソッドでは意味をなさなくなります。

public class ClassValue2 {
    private int s = 3;
    private static int static_count = 0;
    private int instans_count = 0;

    public void add(int num){
        s = num + s;
        static_count = static_count + 1;
        instans_count = instans_count + 1;
    }

    public void substract(int num){
        s = -num + s;
        static_count = static_count + 1;
        instans_count = instans_count + 1;
    }

    public void multiple(int num){
        s = num*s;
        static_count = static_count + 1;
        instans_count = instans_count + 1;
    }
    public void division(int num){
        s = 1/num*s;
        static_count = static_count + 1;
        instans_count = instans_count + 1;
    }

    public void confirmation(){
        System.out.println("static_count:" + static_count);
        System.out.println("instans_count:" + instans_count);
        System.out.println("s:" + s);
    }

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

Java - SpringBootプロジェクトをGAEへデプロイする

構成

今回は以下の構成をそれぞれ作成します

  • Java 8 のスタンダード環境
  • Java 8 のフレキシブル環境

前提

  • Java SE 8 SDK、Java SE 11 SDKのインストールが完了していること
  • Mavenのインストールが完了していること
  • GCPの利用が可能なこと(無料トライアルでもOK)
  • Google Cloud SDKの設定が完了していること

別途まとめていますのでよろしければご参考ください:relaxed:
GCP - 無料トライアルのはじめ方
GCP - Google Cloud SDKのインストール

開発環境の用意

まずは開発環境を用意します。サポートされている開発環境はIntelliJ IDEA、Eclipse(STS)、ビルドツールはApache Maven、Gradleになります。

App Engine コンポーネントのインストール

AppEngineコンポーネントをいれることでローカルの実行環境とデプロイコマンドがインストールされます。

gcloud components install app-engine-java

IDEのプラグインをインストール

IntelliJ IDEA、Eclipse(STS)それぞれ導入方法が異なりますのでこちらを参考にしてください。

今回はSTSを使用するので [Help] > [Eclipse Marketplace] からインストールしました。

スクリーンショット 2019-11-20 13.22.59.png

これでIDE上からApp Engine Projectが作成できるようになります。
(今回は使用しません)

SpringBootプロジェクトの作成

元となるSpringBootプロジェクトを作成します。
ただのHelloWorldプロジェクトなのでお好きなものを使用してください。

新規 > Springスタータープロジェクト

からHelloWorldを作成します。

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class HajibootGcpApplication {

    @GetMapping("/")
    String home() {
        return "Hello world!";
    }

    public static void main(String[] args) {
        SpringApplication.run(HajibootGcpApplication.class, args);
    }

}

ローカルで起動して、動作に問題ないことを確認します。

Java 8 のスタンダード環境

こちらのドキュメントを参考に行います。専用のGCPプロジェクトを事前に作成しておきます。

SpringBootプロジェクトに以下の修正を行います。

  • App Engine Projectへ変更
  • パッケージ方法をwarに変更
  • appengine-maven-pluginの追加
  • ローカルで動作確認
  • プロジェクトの設定とAppEngineアプリを初期化
  • デプロイ

App Engine Projectへ変更

プロジェクトの上で [右クリック] > [Convert to App Engine Standard Project] を選択します。
すると以下のファイルが生成されます。(他のIDEだとこのファイルを作ればいいのかな・・・?)

/src/main/webapp/WEB-INF/appengine-web.xml
<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">

  <threadsafe>true</threadsafe>
  <sessions-enabled>false</sessions-enabled>
  <runtime>java8</runtime>

  <system-properties>
    <property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/>
  </system-properties>

</appengine-web-app>

パッケージ方法をwarに変更

pom.xmlに以下を追記し、パッケージ方法を war に変更します。

pom.xml
<packaging>war</packaging>

メインクラスの実装も以下の通りに変更します。

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
// SpringBootServletInitializerをextends
public class HajibootGcpGaeSe8Application extends SpringBootServletInitializer {

    @GetMapping("/")
    String home() {
        return "Hello world!";
    }

    // configureメソッドをOverride
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(HajibootGcpGaeSe8Application.class);
    }

    public static void main(String[] args) {
        SpringApplication.run(HajibootGcpGaeSe8Application.class, args);
    }

}

appengine-maven-pluginの追加

pom.xmlに以下のプラグインを追加します。PROJECT_IDVERSION をそれぞれ指定します。

pom.xml
<plugin>
    <groupId>com.google.cloud.tools</groupId>
    <artifactId>appengine-maven-plugin</artifactId>
    <version>2.2.0</version>
    <configuration>
        <deploy.projectId>${PROJECT_ID}</deploy.projectId>
        <deploy.version>${VERSION}</deploy.version>
    </configuration>
</plugin>

ローカルで動作確認

以下のコマンドで起動し、http://localhost:8080 へアクセスして動作を確認します。

mvn spring-boot:run

プロジェクトの設定とAppEngineアプリを初期化

以下のコマンドでプロジェクトの設定とApp Engine アプリの初期化を行います。

## プロジェクトの設定
gcloud config set project YOUR_PROJECT_ID

## App Engine アプリの初期化
gcloud app create --project=YOUR_PROJECT_ID

デプロイ

以下のコマンドでデプロイを実行します。

mvn package appengine:deploy

動作確認

ブラウザを起動し、http://YOUR_PROJECT_ID.appspot.com へアクセスするか、以下のコマンドからブラウザを立ち上げます。

gcloud app browse

GCPの画面上からも確認できます。

スクリーンショット 2019-11-27 15.18.09.png

Java 8 のフレキシブル環境

こちらのドキュメントを参考に行います。専用のGCPプロジェクトを事前に作成しておきます。

SpringBootプロジェクトに以下の修正を行います。

  • app.yamlの作成
  • appengine-maven-pluginの追加
  • ローカルで動作確認
  • プロジェクトの設定とAppEngineアプリを初期化
  • デプロイ

app.yamlの作成

app.yamlの設定内容についてはこちらを参照してください。今回はサンプルと同じ内容で設定します。

/src/main/appengine/app.yaml
runtime: java
env: flex

handlers:
- url: /.*
  script: this field is required, but ignored

appengine-maven-pluginの追加

pom.xmlに以下のプラグインを追加します。PROJECT_IDVERSION をそれぞれ指定します。

pom.xml
<plugin>
    <groupId>com.google.cloud.tools</groupId>
    <artifactId>appengine-maven-plugin</artifactId>
    <version>2.2.0</version>
    <configuration>
        <deploy.projectId>${PROJECT_ID}</deploy.projectId>
        <deploy.version>${VERSION}</deploy.version>
    </configuration>
</plugin>

ローカルで動作確認

以下のコマンドで起動し、http://localhost:8080 へアクセスして動作を確認します。

mvn spring-boot:run

プロジェクトの設定とAppEngineアプリを初期化

以下のコマンドでプロジェクトの設定とApp Engine アプリの初期化を行います。

## プロジェクトの設定
gcloud config set project YOUR_PROJECT_ID

## App Engine アプリの初期化
gcloud app create --project=YOUR_PROJECT_ID

デプロイ

以下のコマンドでデプロイを実行します。

mvn package appengine:deploy

動作確認

ブラウザを起動し、http://YOUR_PROJECT_ID.appspot.com へアクセスするか、以下のコマンドからブラウザを立ち上げます。

gcloud app browse

GCPの画面上からも確認できます。

スクリーンショット 2019-11-27 14.21.53.png

無料トライアルを実施している場合はプロジェクトの削除をお忘れなく:wink:

参考

Spring BootでWARを作成して別のTomcatにデプロイする

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

Javaで電卓アプリ作ってみる 〜ウィンドウの中に表示領域を作る

Javaの開発環境は、Ubuntu 18.04にインストールしたOpenJDK 11.0.4を使用しています。

前回の記事では、アプリケーションウィンドウを表示させました。今回はウィンドウの中の表示領域を作っていきます。
前回のコードは、とりあえずJframeを使ってアプリケーションウィンドウの表示が目標だったので、コードの全てをmainメソッドの中に書いてましたが、今後の書きやすさ・読みやすさを考えてMyFrameメソッドを作り、mainメソッドの中でインスタンス化しています。

MyFrame.java
import javax.swing.JFrame;

public class MyFrame extends JFrame{
    public static void main(String[] args) {
        MyFrame frame = new MyFrame("JavaSwingのテスト");
        frame.setVisible(true);
    }

    MyFrame(String title){
        setTitle(title);
        setSize(500, 600);                                // ウィンドウのサイズ (幅, 高さ)
        setLocationRelativeTo(null);                      // ウィンドウを画面中央に表示
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);   // x(バツ)ボタンでアプリケーションを終了させる。
    }
}

JavaSwingでは、土台となるJFrame、その上にパネルやボタンなどを配置するレイヤー構造をしています。
JFrameだけではウィンドウを表示させるだけなので、電卓アプリに必要な数字を表示させるパネルを作ってみます。

MyFrame.java
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.BevelBorder;
import java.awt.FlowLayout;
import java.awt.Dimension;
import java.awt.Color;
import java.awt.Container;



public class MyFrame extends JFrame{
    public static void main(String[] args) {
        MyFrame frame = new MyFrame("JavaSwingのテスト");
        frame.setVisible(true);
    }

    MyFrame(String title){
        setTitle(title);
        setSize(500, 600);                                // ウィンドウのサイズ (幅, 高さ)
        setLocationRelativeTo(null);                      // ウィンドウを画面中央に表示
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);   // x(バツ)ボタンでアプリケーションを終了させる。

        setLayout(new FlowLayout());

        JPanel panelDisplay = new JPanel();                         // パネルのインスタンス化
        panelDisplay.setPreferredSize(new Dimension(500, 60));      // パネルサイズ
        panelDisplay.setBackground(new Color(51, 51, 51));          // カラーコード#333333
        BevelBorder border = new BevelBorder(BevelBorder.RAISED);
        panelDisplay.setBorder(border);

        Container contentPane = getContentPane();
        contentPane.add(panelDisplay);
    }
}

実行結果は、下図のようになりました。
今回は暫定でパネルのサイズを500×60、カラーを#333333としました。
この後ボタンなどの配置などもあるため、サイズ等の変更があるかもしれません。

JavaApp003.png


本記事目次ページ

Javaで電卓アプリ作ってみる

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

Javaで電卓アプリ作ってみる 〜アプリケーションウィンドウを表示させる

まずは、JavaSwingを使って、アプリケーションウィンドウを表示させるところから初めていきます。

Javaの開発環境は、Ubuntu 18.04にインストールしたOpenJDK 11.0.4を使用しています。

JFrameでウィンドウ表示

MyFrame.java
import javax.swing.JFrame;

public class MyFrame extends JFrame{
    public static void main(String[] args) {
        JFrame frame = new JFrame("JavaSwingのテスト");
        frame.setVisible(true);
        // x(バツ)ボタンでアプリケーションを終了させる。
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // ウィンドウの表示位置とサイズ (座標x, 座標y, 幅, 高さ)
        frame.setBounds(300, 80, 500, 600);   
    }
}

このコードを実行すると、指定された位置(x=300, y=80)に指定されたサイズ(500, 600)で空のウィンドウが表示されます。

ウィンドウを画面中央に表示

上記コードのsetBoundsでは、ウィンドウの位置とサイズを指定しましたが、setBoundsを下記コードに置き換えることで、ウィンドウを画面中央に表示させることができます。

// ウィンドウのサイズ (幅, 高さ)
frame.setSize(500, 600);
// ウィンドウを画面中央に表示
frame.setLocationRelativeTo(null);

ウィンドウは。画面中央に表示させたいので、実際に置き換えてみます。

MyFrame.java
import javax.swing.JFrame;

public class MyFrame extends JFrame{
    public static void main(String[] args) {
        JFrame frame = new JFrame("JavaSwingのテスト");
        frame.setVisible(true);
        // x(バツ)ボタンでアプリケーションを終了させる。
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);   
        // ウィンドウのサイズ (幅, 高さ)
        frame.setSize(500, 600);
        // ウィンドウを画面中央に表示
        frame.setLocationRelativeTo(null);
    }
}

実行結果はこちらです。

JavaApp001.png


本記事目次ページ

Javaで電卓アプリ作ってみる

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

Javaで電卓アプリ作ってみる

Javaの開発環境は、Ubuntu 18.04にインストールしたOpenJDK 11.0.4を使用しています。

以前勉強していたJavaを使ってGUIの電卓アプリを作ってみます。
GUIの部分はjavax.swingを使います。

下記目次は、当面の目標としてざっくりしたものを挙げておきます。進行に合わせて随時更新していこうと考えています。

不慣れな部分も多々あると思うので、プログラミング用語など間違った使い方や解釈をしている部分があるかもしれません。その時はアドバイスいただけると幸いです。

目次

  1. アプリケーションウィンドウを表示させる
  2. ウィンドウの中に表示領域を作る。
  3. ボタンの追加

参考サイト

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

JJUG CCC 2019 fallに参加してきました

2019年11月23日(土)に開催されたJJUG CCC 2019 fallに初めて参加してきましたので、感想をまとめてみました。

はじめに

会社の先輩社員に教えていただいたことをきっかけに、JJUGの存在を知りました。
実際に記事にもされているので、ぜひご覧ください。
こちらの記事にJJUGの概要がありますので、JJUGについての説明は省略します。
記事:JJUG CCC 2019 Spring に参加してみた

自己紹介

簡単に自己紹介をさせていただきます。
このICT業界に入ってまだ1年目の新人です。
プログラミングを学び始めたのは、入社してからです。
Javaに触れたのは新入社員研修の時のみで、最近は主にC#の勉強をしていました。

なぜ参加したか

上記で述べたとおり、Javaにはほとんど触れたことがありませんが、エンジニアのコミュニティに興味があったので参加してみました。コミュニティに参加して、ゼロに等しいモチベーションをアップさせなければ…!という思いもありました。
そして、一番の参加理由は気になるセッションがあったことです。これについてはまた後述します。

ステップアップセッション

今回のJJUG CCC 2019 fallからステップアップセッションが追加されました。:clap:
ステップアップセッションとは、Javaを学び始めて数年以内の初級者を対象としているようです。
私が受けたセッションもステップアップセッションです。

ステップアップセッションの感想

  • ステップアップセッションがあることで初参加者、技術的に不安がある人でも安心
  • スピーカーの方が初級者向けを意識して、嚙み砕いて説明してくださる
  • 扱う内容によっては、難しいものもある
  • Javaを学び始めて数年以内を対象としているので、それなりの基礎知識は必要

以上が受けてみての感想です。
ステップアップセッションという名のとおり、自分の学んだ知識を固めたい方、Java初級者から抜け出したい方にはオススメなセッションだと思いました!

Javaで学ぶオブジェクト指向プログラミングの基礎知識 増田亨さん

私がこのJJUGに参加を決めた一番の理由は、このセッションです。

オブジェクト指向…。正直、私は全く理解ができていません。
オブジェクト指向を調べる度に頭を抱えていました。

こちらのセッションでは、オブジェクト指向の基礎について、分かりやすくお話してくださいました。

セッション内容

  • オブジェクト指向はモジュール性とシームレス性の要素がある
  • 型とカプセル化が分かれば、オブジェクト指向が理解しやすくなる
  • 型は値の範囲と操作を制限
  • 型は概念、型をコード化したものがクラス
  • 型をクラスで実体化することをカプセル化 …etc

「型ってあれでしょう…intとかstringとか…」としか思っていなかった私ですが、お話を聞いて型の見方が変わりました!
ずっと謎な存在だったオブジェクト指向について、このセッションのおかげで理解できそうな気がします。
本当に受けて良かったです。

増田さんのtwitterはこちらです : https://twitter.com/masuda220
セッションの資料はこちらにまとまっています : https://github.com/jjug-ccc/slides-articles-2019Fall

最後に

他にもまだまだ魅力的なセッションがありますので、私のような新人の方でもぜひ参加してほしいと思います。

この業界に入った以上は勉強は不可欠になると思うので、1人で勉強するのが難しい方はこのようなイベントはオススメです。
私もそのタイプです。1人だと勉強が続きません…。

最初は参加することに不安はありましたが、とても刺激になりました。
次回もまた参加したいと思います。

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

【初心者向け】ラムダ式とStreamAPIについて

Java8から導入されたラムダ式の記述について備忘録と共に初心者向けに解説していきます。

ラムダ式とは

ラムダ式とは、Java8から導入された、メソッドを変数のように扱うことのできる記述様式のことです。
ラムダ式を使用することで、記述が簡略化でき、より読みやすいコードを書けるようになります。

また、StreamAPIなどの関数型インターフェースを使用する際にラムダ式を使用すると、非常に簡単に記述ができます!!

ラムダ式の記述方法

まずは早速ですが、ラムダ式の実際の記述方法について説明します。

RunnableLamda.java
public static void main(String[] args) {
  Runnable runner = () -> System.out.println("Hello");
  runner.run(); //Hello
}

上記はrunnableインターフェースをラムダ式を用いて記述したものです。
匿名クラスから、さらに「new Runnabl(){}」と「public void run」を省略した形になります。

また、ラムダ式で使用できるのは抽象メソッドが一つのインターフェースのみとなるので注意が必要です。

引数の型はコンパイラーによって自動的に推定されるので記述する必要はありません。ただ、いつも通りに型を記述してもコンパイルエラーになることはありません。
※引数が複数あり、片方だけ省略するとコンパイルエラーになります。

StreamAPI

ラムダ式と同じくJava 8で追加された機能「Stream API」は、ListやCollectionなどの要素の集合に対して処理を行う場合に便利なAPIです。

StreamAPIはラムダ式を前提として作られているので非常に相性が良いです。

例えば、1~6の中で偶数だけを表示する処理は以下のように書くことができます。

StreamLamda.java
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5, 6);
integerList.stream() 
        .filter(i -> i % 2 == 0) 
        .forEach(i -> System.out.println(i)); 

他のStreamAPIも紹介していきます。

forEach

Listの中身をループさせます。非常に簡単に記述できます。

ForEachSample.java
Arrays.asList(new Integer[] { 1, 2, 3, 4, 5, 6 }).forEach(System.out::println);

filter

フィルタリングをするための中間操作で、if文の役目を果たします。
引数には T -> boolean となるラムダ式を渡してあげます。
式がtrueの要素だけを集めます。

FilterSample.java
Arrays.asList(new Integer[] { 1, 2, 3, 4, 5, 6 }).stream().filter(x -> x > 3).forEach(System.out::println);

map

要素を変換できる中間操作で、要素に対して四則演算をしたり、型の変換まで行えます。

MapSample.java
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5, 6);
integerList.stream() 
        .map(i -> "要素は" + i + "です") 
        .forEach(i -> System.out.println(i)); 

最後に

今回紹介したラムダ式やStreamAPIは、従来の書き方で書くこともできます。
しかし、開発現場では自分でコーディングをするだけでなく、他の人が書いたコードを読む機会も多くあります。
他人のプログラムにはラムダ式やStreamAPIが含まれている可能性があるので、慣れておきましょう!

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

Reactive StreamsとJDK 9 Flow APIの入門メモ

概要

この記事はReactive Streamsと、JDK Flow APIを調べたときのメモになります。

Flow API (java.util.concurrent.Flow) はJDK 9(JEP 266)で導入されたAPIで、Reactive Streams Special Interest Group (SIG)というワーキンググループが作成した仕様(Reactive Streams)に対応しています。
この仕様に対応しているJVMのライブラリにはAkka Streams(Lightbend, Inc.)、ReactiveX/RxJavaなどがあり、Spring WebFluxで使われているProject Reactor(Pivotal Software, Inc.)も対応しています。

環境

  • Windows 10 Professional 1909
  • OpenJDK 13.0.1

参考

Reactive Streamsについて

Reactive Streams とは

下記は、Reactive Streamsの冒頭の一文から引用しました。(日本語訳はGoogle翻訳です。)

Reactive Streams is an initiative to provide a standard for asynchronous stream processing with non-blocking back pressure. This encompasses efforts aimed at runtime environments (JVM and JavaScript) as well as network protocols.

Reactive Streamsは、ノンブロッキングバックプレッシャーを伴う非同期ストリーム処理の標準を提供するイニシアチブです。 これには、ランタイム環境(JVMおよびJavaScript)とネットワークプロトコルを対象とした取り組みが含まれます。

この文中にある『ノンブロッキングバックプレッシャーを伴う非同期ストリーム処理 (asynchronous stream processing with non-blocking back pressure)』という一文がReactive Streamsの特徴を端的に表しています。以下に用語集からそれぞれの用語の説明を引用しました。

ノンブロッキング (non blocking) とは

ノンブロッキング

API は、リソースが利用可能ならアクセスさせ、そうでなければ直ちに返って、リソースが現時点では利用できなかったり、操作が開始されて未だ完了していないことを呼び出し元へ伝える。リソースに対するノンブロッキング API では、呼び出し元は、リソースが利用可能になるまでブロックして待つ代わりに他の仕事をすることができる。

バックプレッシャー (back pressure) とは

バック・プレッシャー

過負荷状態のコンポーネントが壊滅的にクラッシュしたり、制御無くメッセージを損失することは許されない。処理が追いつかなくなっていて、かつクラッシュすることも許されないならば、コンポーネントは上流のコンポーネント群に自身が過負荷状態であることを伝えて負荷を減らしてもらうべきだ。このバック・プレッシャー (back-pressure) と呼ばれる仕組みは、過負荷の下でシステムを崩壊させず緩やかに応答を続ける重要なフィードバック機構だ。

非同期 (asynchronous) とは

非同期

リアクティブ宣言の文脈では、「クライアントからサービスへ送信されたリクエストが、送信後の任意の時点で処理されること」を意味する。送信先のサービス内でのリクエスト処理の実行を直接クライアントが観測したり、それに対して同期を取ることはできない。

Reactive Streams Specification for the JVM

SIGが作成したJVM向けの仕様は、2019年11月現在version 1.0.3まで更新されています。

  • version 1.0.0 : 2015年4月30日
  • version 1.0.1 : 2017年8月9日
  • version 1.0.2 : 2017年12月19日
  • version 1.0.3 : 2019年8月23日

成果物

Mavenの成果物として以下のものがありますが、これらは仕様、TCK (Technology Compatibility Kit)、実装例であるため通常のプロジェクトでは直接使用せず、Akka StreamsやReactiveX/RxJava、Reactorなどのライブラリを使用することになると思います。

<!-- https://mvnrepository.com/artifact/org.reactivestreams/reactive-streams -->
<dependency>
    <groupId>org.reactivestreams</groupId>
    <artifactId>reactive-streams</artifactId>
    <version>1.0.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.reactivestreams/reactive-streams-tck -->
<dependency>
    <groupId>org.reactivestreams</groupId>
    <artifactId>reactive-streams-tck</artifactId>
    <version>1.0.3</version>
    <scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.reactivestreams/reactive-streams-tck-flow -->
<dependency>
    <groupId>org.reactivestreams</groupId>
    <artifactId>reactive-streams-tck-flow</artifactId>
    <version>1.0.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.reactivestreams/reactive-streams-examples -->
<dependency>
    <groupId>org.reactivestreams</groupId>
    <artifactId>reactive-streams-examples</artifactId>
    <version>1.0.3</version>
</dependency>

API Components

Reactive StreamsのJVM向け仕様 version 1.0.3で定義されているインターフェースは下記の4つです。

Publisher

Publisherは、無制限または有限の順序付けられた要素(sequenced elements)のプロバイダ(つまり、データストリームの発行)で、Subscriberから(Subscriptionを通じて)要求を受け取ると要素を発行(publish)します。

public interface Publisher<T> {
    public void subscribe(Subscriber<? super T> s);
}
メソッド 説明
subscribe データのストリーミングを開始するようにPublisherに要求をするファクトリメソッド。新しいSubscription毎に複数回呼び出すことができる。

Subscriber

Subscriberは、Publisherから購読(subscribe)した要素を消費(consume)します。このインターフェースのonXxxというメソッドはPublisherからのシグナルに対応しているコールバックメソッドになります。

public interface Subscriber<T> {
    public void onSubscribe(Subscription s);
    public void onNext(T t);
    public void onError(Throwable t);
    public void onComplete();
}
メソッド 説明
onSubscribe Publisher#subscribeを呼び出した後に実行される。Subscriberは引数で受け取ったSubscription を使ってデータを要求、またはキャンセルを行う。
onNext Subscription#requestを呼び出した後に実行される。
onError Publisherのデータ送信が失敗したときに実行される。
onComplete Publisherのデータ送信が正常に終了したときに実行される。(キャンセル含む)

Subscription

Subscriptionは、PublisherとそのPublisherを購読(subscribe)するSubscriberを1対1で表します。SubscriberはSubscriptionのメソッドを介してPublisherへデータ送信またはキャンセルを要求します。

public interface Subscription {
    public void request(long n);
    public void cancel();
}
メソッド 説明
request データを送信するようにPublisherへ要求する。
cancel データの送信を停止しリソースをクリーンアップするようにPublisherへ要求する。

Processor

Processorは、SubscriberとPublisherの両方の機能を持つコンポーネントです。Processorは始端のPublisherと終端のSubscriberの中間に位置しますが、1つだけでなく複数のProcessorを連結して配置することも可能です。

public interface Processor<T, R> extends Subscriber<T>, Publisher<R> {
}

Processorは必ずしも必要というわけではなく、必要がなければ下図のようにPublisherとSubscriberが直接連携をします。

+-----------+              +------------+
|           | <-subscribe- |            |
| Publisher |              | Subscriber |
|           | <--request-- |            |
+-----------+              +------------+

下図は2つのProcessor(A,B)を連結して配置したときのイメージです。
このように中間にProcessorが必要となる状況とは、データストリーム上の途中でフィルタリングやデータ変換を行いたい場合です。

+-----------+              +-----------+              +-----------+              +------------+
|           | <-subscribe- |           | <-subscribe- |           | <-subscribe- |            |
| Publisher |              | Processor |              | Processor |              | Subscriber |
|           | <--request-- |    (A)    | <--request-- |    (B)    | <--request-- |            |
+-----------+              +-----------+              +-----------+              +------------+

実装例

実装例がGitHub(reactive-streams/reactive-streams-jvm)にあります。
下記はPublisher実装例の1つのAsyncIterablePublisherクラスを使ったデモプログラムです。

import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import org.reactivestreams.example.unicast.AsyncIterablePublisher;

import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Slf4j
public class Demo {

  public static void main(String ... args) {
    List<Integer> elements = IntStream.rangeClosed(1, 20).boxed().collect(Collectors.toList());
    ExecutorService executor = Executors.newFixedThreadPool(3);

    AsyncIterablePublisher<Integer> pub = new AsyncIterablePublisher<>(elements, executor);

    MySub mySub1 = new MySub("sub_1");
    MySub mySub2 = new MySub("sub_2");
    MySub mySub3 = new MySub("sub_3");

    log.info("start");

    // Publisher#subscribeを呼び出すと
    // SubscriberのonSubscribeメソッドがコールバックされる
    pub.subscribe(mySub1);
    pub.subscribe(mySub2);
    pub.subscribe(mySub3);

    log.info("end");

    try {
      // 非同期処理のため処理が終了するまで30秒間待機する
      TimeUnit.SECONDS.sleep(30);
      executor.shutdown();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }

  static class MySub implements Subscriber<Integer> {
    private final String name;
    private Subscription s;

    public MySub(String name) {
      this.name = name;
    }

    private Long getId() {
      return Thread.currentThread().getId();
    }

    @Override
    public void onSubscribe(Subscription s) {
      log.info("({}) onSubscribe:[{}]", getId(), name);
      this.s = s;
      // サブスクライブが完了したらデータを発行するようにPublisherへ要求する
      // onSubscribeメソッド内でrequestすることでサブスクライブ完了と同時にデータ発行が始まる
      s.request(1);
    }

    @Override
    public void onNext(Integer integer) {
      // Publisherからデータ発行が行われるとonNextメソッドがコールバックされる
      log.info("({}) onNext:[{}] item:{}", getId(), name, integer);

      // このメソッド内でデータ処理を行う
      // なんらかのデータ処理を行う

      // 次のデータを発行するようにPublisherへ要求する
      s.request(1);

      // もしくはキャンセルする
      //s.cancel();
    }

    @Override
    public void onError(Throwable t) {
      // Publisherのデータ発行にエラーが発生するとコールバックされる
      log.info("onError:[{}]", name);
    }

    @Override
    public void onComplete() {
      // Publisherのデータ発行が完了(若しくはキャンセル)するとコールバックされる
      log.info("({}) onComplete:[{}]", getId(), name);
    }

  }

}

実行結果

[main] INFO Demo - start
[main] INFO Demo - end
[pool-1-thread-2] INFO Demo - (15) onSubscribe:[sub_2]
[pool-1-thread-2] INFO Demo - (15) onNext:[sub_2] item:1
[pool-1-thread-2] INFO Demo - (15) onNext:[sub_2] item:2
[pool-1-thread-3] INFO Demo - (16) onSubscribe:[sub_3]
[pool-1-thread-1] INFO Demo - (14) onSubscribe:[sub_1]
[pool-1-thread-3] INFO Demo - (16) onNext:[sub_3] item:1
[pool-1-thread-2] INFO Demo - (15) onNext:[sub_2] item:3
[pool-1-thread-1] INFO Demo - (14) onNext:[sub_1] item:1
[pool-1-thread-2] INFO Demo - (15) onNext:[sub_2] item:4
[pool-1-thread-1] INFO Demo - (14) onNext:[sub_1] item:2
[pool-1-thread-3] INFO Demo - (16) onNext:[sub_3] item:2
[pool-1-thread-2] INFO Demo - (15) onNext:[sub_2] item:5
[pool-1-thread-1] INFO Demo - (14) onNext:[sub_1] item:3
[pool-1-thread-3] INFO Demo - (16) onNext:[sub_3] item:3
[pool-1-thread-2] INFO Demo - (15) onNext:[sub_2] item:6
[pool-1-thread-1] INFO Demo - (14) onNext:[sub_1] item:4
[pool-1-thread-3] INFO Demo - (16) onNext:[sub_3] item:4
[pool-1-thread-2] INFO Demo - (15) onNext:[sub_2] item:7
[pool-1-thread-1] INFO Demo - (14) onNext:[sub_1] item:5
[pool-1-thread-3] INFO Demo - (16) onNext:[sub_3] item:5
[pool-1-thread-2] INFO Demo - (15) onNext:[sub_2] item:8
[pool-1-thread-1] INFO Demo - (14) onNext:[sub_1] item:6
[pool-1-thread-3] INFO Demo - (16) onNext:[sub_3] item:6
[pool-1-thread-1] INFO Demo - (14) onNext:[sub_1] item:7
[pool-1-thread-2] INFO Demo - (15) onNext:[sub_2] item:9
[pool-1-thread-1] INFO Demo - (14) onNext:[sub_1] item:8
[pool-1-thread-2] INFO Demo - (15) onNext:[sub_2] item:10
[pool-1-thread-3] INFO Demo - (16) onNext:[sub_3] item:7
[pool-1-thread-2] INFO Demo - (15) onNext:[sub_2] item:11
[pool-1-thread-1] INFO Demo - (14) onNext:[sub_1] item:9
[pool-1-thread-3] INFO Demo - (16) onNext:[sub_3] item:8
[pool-1-thread-2] INFO Demo - (15) onNext:[sub_2] item:12
[pool-1-thread-1] INFO Demo - (14) onNext:[sub_1] item:10
[pool-1-thread-3] INFO Demo - (16) onNext:[sub_3] item:9
[pool-1-thread-2] INFO Demo - (15) onNext:[sub_2] item:13
[pool-1-thread-1] INFO Demo - (14) onNext:[sub_1] item:11
[pool-1-thread-3] INFO Demo - (16) onNext:[sub_3] item:10
[pool-1-thread-2] INFO Demo - (15) onNext:[sub_2] item:14
[pool-1-thread-1] INFO Demo - (14) onNext:[sub_1] item:12
[pool-1-thread-2] INFO Demo - (15) onNext:[sub_2] item:15
[pool-1-thread-1] INFO Demo - (14) onNext:[sub_1] item:13
[pool-1-thread-2] INFO Demo - (15) onNext:[sub_2] item:16
[pool-1-thread-2] INFO Demo - (15) onNext:[sub_2] item:17
[pool-1-thread-2] INFO Demo - (15) onNext:[sub_2] item:18
[pool-1-thread-2] INFO Demo - (15) onNext:[sub_2] item:19
[pool-1-thread-1] INFO Demo - (14) onNext:[sub_1] item:14
[pool-1-thread-2] INFO Demo - (15) onNext:[sub_2] item:20
[pool-1-thread-1] INFO Demo - (14) onNext:[sub_1] item:15
[pool-1-thread-2] INFO Demo - (15) onComplete:[sub_2]
[pool-1-thread-1] INFO Demo - (14) onNext:[sub_1] item:16
[pool-1-thread-3] INFO Demo - (16) onNext:[sub_3] item:11
[pool-1-thread-3] INFO Demo - (16) onNext:[sub_3] item:12
[pool-1-thread-3] INFO Demo - (16) onNext:[sub_3] item:13
[pool-1-thread-1] INFO Demo - (14) onNext:[sub_1] item:17
[pool-1-thread-2] INFO Demo - (15) onNext:[sub_3] item:14
[pool-1-thread-1] INFO Demo - (14) onNext:[sub_1] item:18
[pool-1-thread-2] INFO Demo - (15) onNext:[sub_3] item:15
[pool-1-thread-1] INFO Demo - (14) onNext:[sub_1] item:19
[pool-1-thread-2] INFO Demo - (15) onNext:[sub_3] item:16
[pool-1-thread-2] INFO Demo - (15) onNext:[sub_3] item:17
[pool-1-thread-1] INFO Demo - (14) onNext:[sub_1] item:20
[pool-1-thread-2] INFO Demo - (15) onNext:[sub_3] item:18
[pool-1-thread-1] INFO Demo - (14) onComplete:[sub_1]
[pool-1-thread-2] INFO Demo - (15) onNext:[sub_3] item:19
[pool-1-thread-2] INFO Demo - (15) onNext:[sub_3] item:20
[pool-1-thread-2] INFO Demo - (15) onComplete:[sub_3]

JDK Flow API

java.util.concurrent.Flow

FlowクラスにはReactive Streamsの仕様に対応する4つのインターフェースが宣言されています。リアクティブストリームに対応したアプリケーションの開発では、これらのインターフェースを実装する必要があります。

public final class Flow {

    @FunctionalInterface
    public static interface Publisher<T> {
        public void subscribe(Subscriber<? super T> subscriber);
    }

    public static interface Subscriber<T> {
        public void onSubscribe(Subscription subscription);
        public void onNext(T item);
        public void onError(Throwable throwable);
        public void onComplete();
    }

    public static interface Subscription {
        public void request(long n);
        public void cancel();
    }

    public static interface Processor<T, R> extends Subscriber<T>, Publisher<R> {
    }

}

SubmissionPublisher<T>

なお、PublisherについてはSubmissionPublisher<T> という実装クラスがあり、これをそのまま使用するか継承して独自処理を実装するという使い方ができます。

コンストラクタ

コンストラクタ
SubmissionPublisher()
SubmissionPublisher​(Executor executor, int maxBufferCapacity)
SubmissionPublisher​(Executor executor, int maxBufferCapacity, BiConsumer<? super Flow.Subscriber<? super T>,​? super Throwable> handler)
  • maxBufferCapacityは2のべき乗に丸められます。
try (SubmissionPublisher<Integer> pub = new SubmissionPublisher<>()) {
  // 省略
}
try (SubmissionPublisher<Integer> pub = new SubmissionPublisher<>(ForkJoinPool.commonPool(), 8)) {
  // 省略
}
ExecutorService executor = Executors.newFixedThreadPool(3);
try (SubmissionPublisher<Integer> pub = new SubmissionPublisher<>(executor, 8, (subscriber, throwable) -> {
})) {
  // 省略
}

データ発行

SubmissionPublisherクラスにはデータを発行(publish)するメソッドにsubmitofferがあります。

データ発行メソッド
public int submit​(T item)
public int offer​(T item, BiPredicate<Flow.Subscriber<? super T>,​? super T> onDrop)
public int offer​(T item, long timeout, TimeUnit unit, BiPredicate<Flow.Subscriber<? super T>,​? super T> onDrop)

submit

submitはデータ送信できるまでブロックします。

int lag = pub.submit(value);

if (lag < 0) {
  // submitではドロップは発生しない
} else {
  // 最大遅延の推定値(送信されたがまだ消費されていないアイテムの数)
}

offer

offerはデータ送信をブロックせず、送信できなかった場合の処理(再送するかしないか等)を実行できます。
この例では再送せずにデータをドロップします。

int lag = offer(item, (subscriber, value) -> {
  subscriber.onError(new RuntimeException("drop item:[" + integer + "]"));
  return false; // 再送しない
});

if (lag < 0) {
  // ドロップ数
} else {
  // 最大遅延の推定値(送信されたがまだ消費されていないアイテムの数)
}

offer

タイムアウト時間を指定することもできます。この例では送信できなかった場合、1秒まで待機します。

int lag = pub.offer(value, 1, TimeUnit.SECONDS, (subscriber, integer) -> {
  subscriber.onError(new RuntimeException("drop item:[" + integer + "]"));
  return false; // 再送しない
});

if (lag < 0) {
  // ドロップ数
} else {
  // 最大遅延の推定値(送信されたがまだ消費されていないアイテムの数)
}

実装例

下記はSubmissionPublisherクラスを使ったデモプログラムです。

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Flow;
import java.util.concurrent.SubmissionPublisher;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;

@Slf4j
public class Demo {

  public static void main(String ... args) {
    log.info("start");

    MySub<Integer> mySub1 = new MySub<>("sub_1");
    MySub<Integer> mySub2 = new MySub<>("sub_2");
    MySub<Integer> mySub3 = new MySub<>("sub_3");

    ExecutorService executor = Executors.newFixedThreadPool(3);

    try (SubmissionPublisher<Integer> pub = new SubmissionPublisher<>(executor, 256)) {

      pub.subscribe(mySub1);
      pub.subscribe(mySub2);
      pub.subscribe(mySub3);

      log.info("NumberOfSubscribers:{}", pub.getNumberOfSubscribers());
      log.info("MaxBufferCapacity:{}", pub.getMaxBufferCapacity());

      IntStream.rangeClosed(1, 100000).forEach(value -> {
        log.info("publish:{} estimateMinimumDemand:{} estimateMaximumLag:{}", value, pub.estimateMinimumDemand(), pub.estimateMaximumLag());

        int lag = pub.offer(value, 1, TimeUnit.SECONDS, (subscriber, integer) -> {
          log.info("publish offer on drop:{}", integer);
          subscriber.onError(new RuntimeException("drop item:[" + integer + "]"));
          return false; // 再送しない
        });

        if (lag < 0) {
          // ドロップ数
          log.info("drops:{}", lag * -1);
        } else {
          // 最大遅延の推定値(送信されたがまだ消費されていないアイテムの数)
          log.info("lag:{}", lag);
        }

      });

    }

    log.info("end");

    try {
      TimeUnit.SECONDS.sleep(10);

      mySub1.result();
      mySub2.result();
      mySub3.result();

      if (!executor.isShutdown()) {
        log.info("shutdown");
        executor.shutdown();
      }

    } catch (InterruptedException e) {
      e.printStackTrace();
    }

  }

  static class MySub<Integer> implements Flow.Subscriber<Integer> {
    private final String name;
    private AtomicInteger success = new AtomicInteger(0);
    private AtomicInteger error = new AtomicInteger(0);
    private Flow.Subscription s;

    public MySub(String name) {
      this.name = name;
    }

    private Long getId() {
      return Thread.currentThread().getId();
    }

    @Override
    public void onSubscribe(Flow.Subscription subscription) {
      log.info("({}) onSubscribe:[{}]", getId(), name);
      this.s = subscription;
      s.request(1);
    }

    @Override
    public void onNext(Integer item) {
      log.info("({}) onNext:[{}] item:{}", getId(), name, item);
      success.incrementAndGet();
      s.request(1);
    }

    @Override
    public void onError(Throwable throwable) {
      log.info("({}) onError:[{}]", getId(), name);
      error.incrementAndGet();
    }

    @Override
    public void onComplete() {
      log.info("({}) onComplete:[{}]", getId(), name);
    }

    public void result() {
      log.info("result:[{}] success:{} error:{}", name, success.get(), error.get());
    }

  }

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

Spring のBean Validaiton 〜びーん ばりでーしょん〜

Bean Validation

Spring Frameworkを使用し、REST通信を基本としたサンプルを記載する。

びーん ばりでーしょんって?

クライアントから送信されたRequestBodyの値をチェックする。
チェック内容は何種類かある。

  • Null
  • 空文字
  • 文字数
  • パターン(正規表現)
  • 最大サイズ
  • 最小サイズ

などなど...

とりあえずサンプル

コントローラでただバリデーションを実行するだけのサンプル。

SampleContorller.java
@RestController
@RequestMapping("validation")
public class SampleController {

  // バリデーションするだけ
  @PostMapping
  public SampleResource validation(
        @RequestBody @Validated SampleResource resource) {
    return resource;
  }
}
@Getter
@Setter
public class SampleResource {
  @NotNull
  @Size(min = 5, max = 30)
  @Pattern(regexp = "[a-zA-Z0-9]*")
  private String message;
}
アノテーション 説明
@NotNull Nullを禁止
@Size {min}以上{max}以下であることを確認する
@Pattern 指定パターンであることを確認する

more びーん ばりでーしょん

ネストしたクラスのバリデーション

フィールドに@Validを指定する。

SampleContorller.java
上のサンプルコントローラ一緒
@Getter
@Setter
public class SampleResource {
  @Valid
  @NotNull
  private SampleNestResource nestResource;
}

@Validでネストしたクラスのバリデーションを実行している。nestResourceが送信されてこない場合はフィールドにNullがバインドされてしまい、SampleNestResourceに指定したバリデーションが実行されないため、@NotNullで必須にしている。

@Getter
@Setter
public class SampleNestResource {
  @NotNull
  @Size(min = 5, max = 30)
  @Pattern(regexp = "[a-zA-Z0-9]*")
  private String message;
}

Collection内のチェック

ListなどのCollectionをフィールドに指定した場合、Collectionないのクラスのバリデーション指定とフィールドに対するバリデーションの指定が異なる。

SampleContorller.java
上のサンプルコントローラ一緒
@Getter
@Setter
public class SampleResource {

  @Size(min = 2)
  private List<@NotNull String> strList;
}

@SizeでフィールドのList要素が2以上かを確認
@NotNullList内のStringがNullではないことを確認

バリデーションエラーのハンドリング

ハンドリングを行わない場合はMethodArgumentNotValidExceptionが発生する。
RestControllerではBindingResultにエラーが入らないため、Errorsでエラー内容を取得する。

SampleContorller.java
@RestController
@RequestMapping("validation")
public class SampleController {

  // バリデーションするだけ
  @PostMapping
  public SampleResource validation(
        @RequestBody @Validated SampleResource resource,
        Errors errors) {
    if (errors.hasErrors()){
      throw new RuntimeException();
    }

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

JJUG CCC 2019 Fallに参加してきました.

JJUG CCC 2019 Fallに参加してきました.

Javaを初めて半年,イベントに初参加!!

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

発表者の資料

  • オブジェクト指向の考え方がソフトウェア開発に活かせる
  • 増田さんの考えるオブジェクト指向プログラミングのこだわり
    • モジュール性
      • 型(値の種類)でプログラムを分割
      • 型でモジュールを作る
    • シームレス性
      • 一連の活動の継ぎ目をなくす開発手法
  • 持ち帰ってほしい言葉
    • カプセル化
  • 2つのモード
    • モード1
      • 定義済みの型だけを使う
      • 標準ライブラリや組み込み型のみ
      • 型の消費者
    • モード2
      • 独自の型を定義する
      • 型の生産者
  • オブジェクト指向プログラミングのスキルアップとは
    • モード1からモード2へ移行すること
    • クラス設計のスキル
    • 独自の型の発見と改良のスキル
  • まとめ
    • 型という概念的なものをクラスへカプセル化
    • クラスには値の範囲の定義と値の操作の定義を与え具体化

感想

事前に考えてた内容とは全然違ったけどかなり面白かった

13:30~14:15 開け!ドメイン駆動設計の扉

発表者の資料(無いよ)

概要

ドメイン駆動設計とは何か?
どう良いのか?
どのようにやったらいいのか?(ここら辺は速すぎて全然メモれなかった)

聞いた時のメモ

  • モチベーション
    • なぜドメイン駆動設計か?
    • 非ドメイン駆動設計とは
      • コードの森で迷う
      • 関連する実装が散らばる
  • 目的
    • ソフトウェアの利用者とコードが地続きになること
    • 開発速度ではなく,保守性を高めるプラクティス
    • ドメイン駆動設計とは
    • ドメインとは
      • ソフトウェアに含まれる範囲はどこか
    • ドメインとコードがモデルを通して,つながる
      • 反復的な開発になる
  • 知識の上流
    • ドメインエキスパート
      • 作業の主体者
      • 重要なことを知っている人
      • 開発者がドメインを決める上で,相談する人
    • 開発者はドメインエキスパートと会話が必要
    • ユビキタス言語を使う
      • だれにでも通じる言葉
    • 相手の言語を使うことで,理解
      • 互いの理解を深める
  • 構成要素
    • 早すぎてメモれない..
  • 深いモデルへ

    • 始めから全ては把握できない
    • 会話の力が大事
    • 刹那的なニュアンスをキャッチする
    • リファクタリングを嫌わない
  • まとめ

    • ドメイン駆動設計とは当たり前のことを当たり前に実践するためのプラクティス

感想

「Javaで学ぶオブジェクト指向プログラミングの基礎知識」と通じる話もあり,面白かった.
速すぎて付いていけない点も多かった.

14:30~15:15 JUnit再入門

発表者の資料

メモ

  • なぜテストコードを書くのか
    • 回帰テストが可能になるから
    • 高い信頼性
    • 属人性を残さない
    • デバック完了を実装時に確認
    • デバックが楽しい
      • 人間に間違いを指摘されると心理的なダメージが....
  • テストコツ
    • テストを先に書く
      • テスト失敗を確認してから実装
      • 失敗を担保してから実装
    • 分かりやすいテスト名を付ける
      • 日本語でテスト名をかく
      • チームのメンバーが全員日本語に堪能であることが条件
    • assertionを書き過ぎない
      • ほどほどにまとまった単位で書く
      • 必要に応じてassertAllを利用
    • カバレッジ(網羅度)にこだわり過ぎない
      • 実証済みコードはテストしない
      • getter,setterのテストは不要

感想

前半部分は初心者向けの内容,後半部分が大事だと感じた
現状,テストファーストには開発できていないため,改善したいと感じた.
特にUIのテストに取り組みたい.

16:45~17:30 新卒3年目が立ち向かったお名前.comでの超巨大レガシーシステム脱却の事例

発表者の資料

メモ

  • レガシーなシステムとは
    • 意図したとおりには動作
    • 内部のコードが複雑で,メンテしずらい
    • 保守コストが高い
  • レガシーなシステムからの脱却の問題点
    • 仕様に関するドキュメントがない(仕様)
    • 複雑化している箇所があり,修正が困難(実装)
    • ピュアなJavaで実装
    • 意図の分かりずらい実装
    • 生クエリが定義
    • 設定情報がべた書き
    • クラスの責任があいまい
    • 単体テストのコードがない(テスト)
  • 解決方法
    • 仕様に関するドキュメントがない(仕様)
    • wikiにまとめて共有
    • gitにマジリク時にコードの詳細な情報を残す
    • 複雑化している箇所があり,修正が困難(実装)
    • ピュアなJavaで実装
      • JDKの選定
      • フレームワークの選定
      • Web APIアーキティクチャの選定
    • 意図が分かりずらい実装
      • コードレビューの実施による属人性の防止
      • フレームワークに従った標準的な実装
    • 生クエリが定義
      • フレームワークの導入
    • 設定情報がべた書き
      • 設定情報をYALMに統一
    • クラスの責任
      • 各レイヤーごとに責任を明確化
      • 他のモジュールに影響しない設計
    • 単体テストのコードがない
    • JUnitの導入
    • Mockitoの導入
  • 今後に向けて
    • ルールの厳守や定期的なメンテが必要
    • さもないと,レガシー化する

感想

社内のシステムを眺めることが多いですが,実装が分かりずらいことが多い
レガシーなシステムとの戦う機会は多いと感じている.(いずれ改修も..)
戦い方の参考にさせていただきます!!

イベント全体の感想

初心者向けの講演に参加していたが,情報量が多く付いていけないことが多かった.
→スライド枚数を制限するなど,情報量を絞ってほしい
お昼ご飯が豪華でビックリした
→おいしく頂きました.ありがとうございました.
Gradleのお話も聞きたかった
Gradle を完全に理解した人が、何も分からなくなるための第一歩
UIのテストの話を重点的に聞いてみたい
←今関心があるから

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

テストコードを書くことは、ライブラリなどの動作確認にもいいぞ

皆が抱くであろう不安

ライブラリのメソッドなどで挙動がわからない、どう動くか不安だ。。。
といったこと、ありませんか?

ドキュメントが公開されていればそれを読むのも大切ですが、多くの人はいきなりプロダクトのコードに組み込まず、挙動を試すと思います。

そういった時にただ手元で試すだけでなく、テストコードとして残るように試すことをおすすめします。

以下、Kotlinで例を見ていきましょう。

String.trim()のテストを書いてみる(例)

kotlin.stdlibには、fun String.trim(): String という、文字列の前後の空白文字を消してくれる便利なメソッドが用意されています。

公式ドキュメント によると、以下のように書かれています。

Returns a string having leading and trailing whitespace removed.

whitespace とありますが、これが「半角スペースのみならず、全角スペースなどは対象になるのかならないのか?」わからなかったりします。
こういう時にテストコードを書いてみます。

TrimTest.kt
@Test
fun trim_FullWidth() {
    val actual = " 全角スペースのテスト ".trim()
    val expected = "全角スペースのテスト"
    assertThat(actual, `is`(expected))
}

このテストは成功します。
つまり、全角スペースを除外対象(whitespace)に含むことがわかります。

他にも、「タブは空白文字として扱われるのか?」や、「複数空白文字がある場合はどうなるか?」なども、同様にテストコードを書けば挙動を確認できます。

TrimOthersTest.kt
@Test
fun trim_Tab() {
    val actual = "\tタブのテスト\t".trim()
    val expected = "タブのテスト"
    assertThat(actual, `is`(expected))
}

@Test
fun trim_SomeWhiteSpaces() {
    val actual = " \t さまざまな空白文字のテスト \t\t   ".trim()
    val expected = "さまざまな空白文字のテスト"
    assertThat(actual, `is`(expected))
}

まとめ

もちろんライブラリのメソッド自体のテスト1(上記の例のテスト)をプロダクトコードで直接書くことは少ないかもしれませんが、trim()を内部で利用するメソッドをプロダクトコードで書いた時には、このテストケースは十分プロダクトのコードに活かせます。

また、手を動かしながら動作確認と学習ができますし、何よりコードとして残すことができ、書いたテストケースに関しては、その挙動は正しいことがわかります。

おまけ

Javaのtrim()は全角スペースが対象外です。以下のテストを書いてみると、テストに失敗し、対象外であることがわかります。

TrimTest.java
@Test
public void trim_FullWidth() {
    String actual = " 全角スペースのテスト ".trim();
    String expected = "全角スペースのテスト";
    assertThat(actual, is(expected));
}

  1. 学習用テストというものがあるようです。また、そもそもライブラリにそのメソッドのテストコードがある場合もあります。直接見に行くこともいいと思いますが、自分で書いて動作を試すという意味では意義があるかと。テストコードがない場合はOSSに貢献するチャンスです!すかさずプルリクを送りましょう! 

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