20200528のJavaに関する記事は11件です。

Google ColaboratoryでJavaのJupyter Kernelを使う

前回(Google ColaboratoryでJavaプログラムを動かす)の続き

前回との違い

ちゃんとColab上でJavaのJupyter Kernelを使ってpythonを使うのと同じようにJavaを動かす

手順

1. colab上から.ipynbをダウンロードする

そのまま
image.png

2. .ipynbを書き換える

元の内容は下記のようになっている

ColabJava.ipynb
{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "ColabJava.ipynb",
      "provenance": []
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    }
  },
  "cells": [
    {
      "cell_type": "code",
      "metadata": {
        "id": "hXaCsZGNbimN",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        ""
      ],
      "execution_count": 0,
      "outputs": []
    }
  ]
}

kernelspecを書き換える

変更後.ipynb
    ...
    "kernelspec": {
      "name": "java",
      "display_name": "Java"
    }
    ...

Jupyter Kernelを入れる

変更した.ipynbをノートブックをアップロードからアップロード
今回はIJavaを使用するので、開いたcolabで下記セルを実行

!git clone https://github.com/SpencerPark/IJava.git
%cd IJava/
!./gradlew installKernel

最初はJava Kernelが無い状態なので下記のようなメッセージが左下に表示される
image.png
ので、上のセルが実行完了したらノートブックの設定を改めてJavaに合わせて保存し直す。

完全勝利!

image.png

おまけ(外部ライブラリを使う)

今回使用したIJavaのRead meにもちゃんと書いてあるが、外部ライブラリも普通に読んで使える。(すごい!)

image.png

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

try-with-resourcesとAutoCloseable

try-with-resourcesは大体IOExceptionが発生するものに対してのみ使えると思っていましたが、。
AutoCloseableで様々なクラスに対して有効化できるらしい。

try-with-resourcesについて

try-with-resourcesステートメントは、1つ以上のリソースを宣言するtryステートメントです。 リソースは、プログラムが終了した後に閉じる必要があるオブジェクトです。 try-with-resourcesステートメントは、各リソースがステートメントの最後で確実に閉じられるようにします。 java.io.Closeableを実装するすべてのオブジェクトを含む、java.lang.AutoCloseableを実装するすべてのオブジェクトをリソースとして使用できます。

Oracle Java Documentation
https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html

つまり、AutoCloseableを実装すればtry-with-resourcesに対応できる!

AutoCloseableについて

閉じられるまでリソース(ファイルまたはソケットハンドルなど)を保持できるオブジェクト。
AutoCloseableオブジェクトのclose()メソッドは、リソース仕様ヘッダーでオブジェクトが宣言されているtry-with-resourcesブロックを終了すると、自動的に呼び出される。
この構造により、迅速なリリースが保証され、リソース不足の例外や、そうでなければ発生する可能性のあるエラーが回避されます。
java.langパッケージに含まれている。

Interface AutoCloseable
https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/lang/AutoCloseable.html

try-with-resourcesでtry文が終わった時に自動でクローズ処理してもらいたい、
またクローズ時に特殊な処理を挟んでほしい対象のリソースに対してAutoCloseableインターフェースを実装する。

AutoCloseableとCloseableについて

AutoCloseableと似て非なるものでCloseableがある。
こちらはAutoCloseableを継承したインターフェースであり、こちらはjava.ioパッケージに含まれている。
AutoCloseableは例外発生時にExceptionをスローするが、CloseableはIOExceptionをスローする。

java.ioパッケージにあるほとんどのクラスでCloseableが実装されている為、
FileOutputStreamやBufferedReaderなどのクラスはtry-with-resourcesで宣言できる。

使用

try-with-resourcesで自動的にリソースの開放したい場合、
リソース解放時にほかの処理も加えたい場合などに使用する。

SampleTryWithResources.java
public class SampleTryWithResources {
    public static void main(String[] args) {

//     Closeable(AutoCloseable)を実装しているクラスを
//    try-with-resourcesで宣言すると、try文終了時に自動でclose()が実行される。
        try (SampleCloseAble sca = new SampleCloseAble()) {
            System.out.println("Try-With-Resources開始");

            System.out.println("try文実行中");

            System.out.println("Try-With-Resources終了");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class SampleCloseAble implements AutoCloseable {
    @Override
    public void close() throws Exception {
        System.out.println("Closeableクラスのclose()実行");
    }
}
sys.out
Try-With-Resources開始
try文実行中
Try-With-Resources終了
Closeableクラスのclose()実行

try文の中の処理が終了した後に実装したcloseメソッドが呼び出されている。
ただし、次のようにtry文の中で例外が発生すると、catch文の前にcloseメソッドが呼び出されることに注意すること。

SampleTryWithResources2.java
public class SampleTryWithResources2 {
    public static void main(String[] args) {

        try (SampleCloseAble sca = new SampleCloseAble()) {
            System.out.println("Try-With-Resources開始");

            System.out.println("try文実行中");

            throw new Exception();
        } catch (Exception e) {
            System.out.println("Try文例外終了");
            e.printStackTrace();
        }
    }
}
sys.out
Try-With-Resources開始
try文実行中
Closeableクラスのclose()実行
Try文例外終了
java.lang.Exception
    at ...

処理の順番は
Try文の中の処理 ⇒ AutoCloseableのcloseメソッド ⇒ catch文の中の処理
となる。

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

「ソースが見つかりませんでした」eclipseでデバッグした時に出てきたら

eclipseでspringbootを使って開発している駆け出しエンジニアです。
やり方を調べながらデバッグをやろうとした時に出てきたエラーの解決方法についてです。

状況・原因

ブレークポイントを立てて、デバッグを起動しました。

ブレークポイントで止まったと思ったら、赤文字で
「ソースが見つかりませんでした」と表示されました。

ソースルックアップパスというものに動かしているプロジェクトが含まれていなかったことが原因でした。

解決方法

  • デバッグの所の虫みたいなマークの隣にある逆三角クリック

  • →デバッグの構成→ソースと進む

  • →ソースルックパスで「追加」を押して、使ってるJavaプロジェクトを追加する

僕はこれで無事に解決されました。
試してみてください。

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

JavaFXのTableViewで最後の列を自動制御にする

概要

JavaFXのTableViewの列幅の制御で、困ったのでそれの対応方法のメモ。

最後の列を自動制御にして、テーブル幅いっぱいに列が埋まるようにしてみた。

ColumnResizePolicy

JavaFxのTableViewの列幅の制御については、ColumnResizePolicyという仕組みがある。

これは、初期描画時や列幅の調整時に、どのように列幅の制御をするかを決めている仕組みになるようだ。

デフォルトで用意されているのは2種類

定数 概要
UNCONSTRAINED_RESIZE_POLICY 特に制御は無く、ユーザーが動かした通りに列幅を処理する
CONSTRAINED_RESIZE_POLICY 初期描画時などは均等の列幅、列の伸縮時はそれに合わせて、他の列を均等に処理する 

これ以外で独自の処理が必要な場合は実装する必要があり、実態としてはCallbackクラスとなる。

public final void setColumnResizePolicy(Callback<ResizeFeatures, Boolean> callback)

この中で、各列の幅の変更を行い、成功した場合はtrueを返すことで実現しているようだ。

個人的に欲しかったのは、最後の列の幅の自動制御で、以下を実現したい

- 初期描画時、ウィンドウのリサイズ時は、最後の列をウィンドウ幅(ペイン)に合わせたい
- ダブルクリックのオートフィット時にも、最後の列をウィンドウ幅(ペイン)に合わせたい

JavaFxのTableView周りを覗いてどうにか出来ないか探したが、デフォルトではないようで、あきらめて実装。
何とか目的の動作になった。細かい制御や考慮はしてないけど、動作確認としては問題なさそう。

public class ResizePolicy {

    public static <S> void setLastColumnResizePolicy(TableView<S> tableView) {
        // リサイズポリシーをセットする
        tableView.setColumnResizePolicy(prop -> {
            if (prop.getTable().getWidth() < 0) {
                return false;
            }
            resizeTargetColumn(prop);
            resizeLastColumn(prop);
            return true;
        });

        // ダブルクリック時のオートフィット後のリサイズ処理を追加する
        addResizeBorderDoubleClickedEvent(tableView);
    }

    private static void resizeTargetColumn(TableView.ResizeFeatures<?> prop) {
        var targetColumn = prop.getColumn();
        var delta = prop.getDelta();

        if (delta != 0 && targetColumn.isResizable()) {
            var newWidth = targetColumn.getWidth() + delta;

            // 最大幅、最小幅を考慮して、最終的な幅を設定する
            if (newWidth > targetColumn.getMaxWidth()) {
                targetColumn.setPrefWidth(targetColumn.getMaxWidth());
            } else if (newWidth < targetColumn.getMinWidth()) {
                targetColumn.setPrefWidth(targetColumn.getMinWidth());
            } else {
                targetColumn.setPrefWidth(newWidth);
            }
        }
    }

    private static void resizeLastColumn(TableView.ResizeFeatures<?> prop) {
        var table = prop.getTable();
        // -18.0 をしないとスクロールバーが出る。18.0は画面を見て決めただけ
        var tableContentWidth = table.getWidth() - 18.0;

        // 最後のカラム以外の合計値とテーブルの幅から、最後のカラムの幅を算出する
        var columns = table.getVisibleLeafColumns();
        var columnsOfExcludedLastColumn = columns.stream().limit(columns.size() - 1);
        var widthsOfExcludedLastColumn = columnsOfExcludedLastColumn.map(TableColumnBase::getWidth);
        var lastColumnWidth = widthsOfExcludedLastColumn
                .reduce(tableContentWidth, (remainingWidth, columnWidth) -> remainingWidth - columnWidth);

        if (lastColumnWidth > 0) {
            var lastColumn = columns.get(columns.size() - 1);
            lastColumn.setPrefWidth(lastColumnWidth);
        }
    }

    private static <S> void addResizeBorderDoubleClickedEvent(TableView<S> tableView) {
        // 画面描画時にはskinがnullなので、イベントとしてやりたいことを登録しておく
        tableView.skinProperty().addListener((observable, oldValue, newValue) -> {
            var tableViewSkin = (TableViewSkin<?>) newValue;
            var tableViewSkinNodes = tableViewSkin.getChildren().stream();
            var tableHeaderRowNode = tableViewSkinNodes.filter(node -> node instanceof TableHeaderRow).findFirst();

            tableHeaderRowNode.ifPresent((node) -> {
                var tableHeaderRow = (TableHeaderRow) node;
                var tableColumnHeader = (NestedTableColumnHeader) tableHeaderRow.getChildren().get(1);

                tableColumnHeader.addEventFilter(MouseEvent.MOUSE_PRESSED, mouseEvent -> {
                    // カラム幅の変更については、四角形の線が実際のイベント対象になる
                    // かつダブルクリックのオートフィットを対象とする
                    if (isRectangle(mouseEvent) && isDoubleClick(mouseEvent)) {

                        // クリックされた四角形の線の位置と、カラム幅から対象となるカラムを探し出す
                        var rectangle = (Rectangle) mouseEvent.getTarget();
                        var columnPosition = 0.0;
                        for (var column : tableView.getColumns()) {
                            columnPosition += column.getWidth();
                            if (rectangle.getLayoutX() < columnPosition) {
                                // このタイミングでリサイズポリシーを実行すると、
                                // オートフィットによる幅の変更前なので、実際の実行は後回しにする
                                Platform.runLater(() -> {
                                    var columnResizePolicy = tableView.getColumnResizePolicy();
                                    columnResizePolicy.call(new TableView.ResizeFeatures<S>(tableView, column, -0.1));
                                });
                                break;
                            }
                        }
                    }
                });
            });
        });
    }

    private static boolean isRectangle(MouseEvent mouseEvent) {
        return mouseEvent.getTarget() instanceof Rectangle;
    }

    private static boolean isDoubleClick(MouseEvent mouseEvent) {
        return mouseEvent.getClickCount() == 2 && mouseEvent.isPrimaryButtonDown();
    }

}

本当は、staticではなく、クラスとして作ろうとしたが、ジェネリクスの問題で警告が出て、SuppressWarningsで消すのもなんだかなと思って、staticなメソッドにしてしまった。。。

ResizeFeaturesはジェネリクスのクラスなのに、setColumnResizePolicyメソッドでは、生で扱ってるので、自分で型宣言をちゃんとやると、型エラーで受け入れてくれない。

型を付けないと警告が出る感じです。

なんでこうしたんだろうか。。。

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

Spring Bootで簡易検索アプリ作成

標題の通り、検索アプリを作成しました。

使用環境

・Windows10 (64bit)
・spring-boot:2.2.6
・Eclipse:4.9.0
・H2

完成画面

ジャンル、著者、タイトルのいずれかまたは、すべて入力した際に
画面の下部にある内容を引っ張ってきます。
image.png

Entity

BookData.java
import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class BookData {

    @Id
    private long isbn;

    private String genre;
    private String author;
    private String title;
    private int stock;
    private Boolean status;

    public long getIsbn() {
        return isbn;
    }
    public void setIsbn(long isbn) {
        this.isbn = isbn;
    }
    public String getGenre() {
        return genre;
    }
    public void setGenre(String genre) {
        this.genre = genre;
    }
    public String getAuthor() {
        return author;
    }
    public void setAuthor(String author) {
        this.author = author;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public int getStock() {
        return stock;
    }
    public void setStock(int stock) {
        this.stock = stock;
    }
    public Boolean getStatus() {
        return status;
    }
    public void setStatus(Boolean status) {
        this.status = status;
    }
}

Dao

ジャンル、著者、タイトルで検索するためDaoクラスを用意します。

BookDataDao.java
import java.io.Serializable;
import java.util.List;

public interface BookDataDao extends Serializable {

    public List<BookData> search(String genre, String author, String title);
}

Repository

Daoクラスを実装したBookDataDaoImplを作成します。
各記述の内容はコメントを参照ください。

BookDataDaoImpl.java
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

@Repository
public class BookDataDaoImpl implements BookDataDao {

    //Entityを利用するために必要な機能を提供する
    @Autowired
    private EntityManager entityManager;

    public BookDataDaoImpl() {
        super();
    }

    public BookDataDaoImpl(EntityManager manager) {
        this();
        entityManager = manager;
    }

    //Daoクラスで用意したsearchメソッドをオーバーライドする
    @SuppressWarnings("unchecked")
    @Override
    public List<BookData> search(String genre, String author, String title) {

        //StringBuilderでSQL文を連結する
        StringBuilder sql = new StringBuilder();
        sql.append("SELECT b From BookData b WHERE ");

        boolean genreFlg  = false;
        boolean authorFlg = false;
        boolean titleFlg  = false;
        boolean andFlg    = false;

        //genreがブランクではなかった場合、sql変数にappendする
        //フラグをtrueにしとく
        if(!"".equals(genre)) {
            sql.append("b.genre LIKE :genre");
            genreFlg = true;
            andFlg   = true;
        }

        //authorがブランクではなかった場合、sql変数にappendする
        //フラグをtrueにしとく
        if(!"".equals(author)) {
            if (andFlg) sql.append(" AND ");
            sql.append("b.author LIKE :author");
            authorFlg = true;
            andFlg    = true;
        }

        //titleがブランクではなかった場合、sql変数にappendする
        //フラグをtrueにしとく
        if(!"".equals(title)) {
            if (andFlg) sql.append(" AND ");
            sql.append("b.title LIKE :title");
            titleFlg = true;
            andFlg   = true;
        }

        /*
        QueryはSQLでデータを問い合わせるためのクエリ文に相当する機能を持つ
        entityManagerのcreateQueryメソッドを使用する
        sql変数を引数に渡す
        */
        Query query = entityManager.createQuery(sql.toString());

        //上記のif文でtrueになった場合は、各変数に値をセットする
        //今回、あいまい検索したいのでlike句を使用する
        if (genreFlg) query.setParameter("genre", "%" + genre + "%");
        if (authorFlg) query.setParameter("author", "%" + author + "%");
        if (titleFlg) query.setParameter("title", "%" + title + "%");
        return query.getResultList();
    }
}

BookDataRepository

BookDataRepository.java
import org.springframework.data.jpa.repository.JpaRepository;

public interface BookDataRepository extends JpaRepository<BookData, Long> {

}

Service

BookService.java
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class BookService {

    @Autowired
    private BookDataRepository bookDataRepository;

    @Autowired
    private BookDataDaoImpl bookDataDaoImpl;


    //全件検索
    public List<BookData> findAll(){
        return bookDataRepository.findAll();
    }

    //該当のID見つける
    public Optional<BookData> findById(long isbn) {
        return bookDataRepository.findById(isbn);
    }

    //保存
    public BookData save(BookData bookData) {
        return bookDataRepository.saveAndFlush(bookData);
    }

    //検索
    public List<BookData> search(String genre, String author, String title){

        List<BookData> result = new ArrayList<BookData>();

        //すべてブランクだった場合は全件検索する
        if ("".equals(genre) && "".equals(author) && "".equals(title)){
            result = bookDataRepository.findAll();
        }
        else {
            //上記以外の場合、BookDataDaoImplのメソッドを呼び出す
            result = bookDataDaoImpl.search(genre, author, title);
        }
        return result;
    }
}

Controller

StockController.java
import java.util.List;
import java.util.Optional;

import javax.annotation.PostConstruct;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/book")
public class StockController {

    @Autowired
    private BookService bookService;

    @PersistenceContext
    EntityManager entityManager;

    //一覧表示処理
    @GetMapping
    public String index(Model model,@ModelAttribute("formModel") BookData bookdata) {

        model.addAttribute("msg", "在庫管理");
        model.addAttribute("msg2", "検索条件を入力してください");
        List<BookData> books = bookService.findAll();
        model.addAttribute("books", books);

        return "index";
    }

    //検索結果の受け取り処理
    //@ModelAttributeでformからformModelを受け取り、
    //その型(BookData)と変数(bookdata)を指定する
    @PostMapping
    public String select(@ModelAttribute("formModel") BookData bookdata, Model model) {

        model.addAttribute("msg", "検索結果");
        //bookdataのゲッターで各値を取得する
        List<BookData> result = bookService.search(bookdata.getGenre(),bookdata.getAuthor(), bookdata.getTitle());
        model.addAttribute("books", result);

        return "index";
    }

    //詳細画面処理
    //@PathVariableでURLから受け取った値を取得する
    @GetMapping("detail/{isbn}")
    public String detail(@PathVariable long isbn, Model model) {

        model.addAttribute("msg", "参照画面");
        Optional<BookData> data = bookService.findById(isbn);
        //Optionalを使用する際、値はget()で取得する
        model.addAttribute("form", data.get());

        return "detail";
    }


    //初期化処理
    @PostConstruct
    public void init() {

        BookData d1 = new BookData();
        d1.setAuthor("夏目漱石");
        d1.setTitle("こころ");
        d1.setGenre("文学");
        d1.setIsbn(11111);
        d1.setStock(100);
        d1.setStatus(false);
        bookService.save(d1);

        BookData d2 = new BookData();
        d2.setAuthor("大野次郎");
        d2.setTitle("Spring入門");
        d2.setGenre("技術");
        d2.setIsbn(22222);
        d2.setStock(1);
        d2.setStatus(false);
        bookService.save(d2);

        BookData d3 = new BookData();
        d3.setAuthor("田中太郎");
        d3.setTitle("ネットワークのしくみ");
        d3.setGenre("技術");
        d3.setIsbn(33333);
        d3.setStock(20);
        d3.setStatus(false);
        bookService.save(d3);

        BookData d4 = new BookData();
        d4.setAuthor("吉川量");
        d4.setTitle("泥の銃弾");
        d4.setGenre("ミステリー");
        d4.setIsbn(44444);
        d4.setStock(99);
        d4.setStatus(false);
        bookService.save(d4);

        BookData d5 = new BookData();
        d5.setAuthor("夏目漱石");
        d5.setTitle("草枕");
        d5.setGenre("文学");
        d5.setIsbn(55555);
        d5.setStock(40);
        d5.setStatus(false);
        bookService.save(d5);

        BookData d6 = new BookData();
        d6.setAuthor("テスト敏郎");
        d6.setTitle("連続殺人事件簿");
        d6.setGenre("ミステリー");
        d6.setIsbn(66666);
        d6.setStock(40);
        d6.setStatus(false);
        bookService.save(d6);

        BookData d7 = new BookData();
        d7.setAuthor("蛇場太郎");
        d7.setTitle("Java入門");
        d7.setGenre("技術");
        d7.setIsbn(77777);
        d7.setStock(40);
        d7.setStatus(false);
        bookService.save(d7);
    }
}

index.html

index.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>初期画面</title>
<style>
h1 {font-size:18pt; font-weight:bold; color:gray;}
body {font-size:13pt; font-weight:bold; color:gray; margin:5px 25px;}
tr {margin:5px;}
th {padding:5px;color:white; background:darkgray;}
td {padding:5px;color:black; background:#f0f0f0;}
</style>
</head>
<body>
    <h1 th:text="${msg}"></h1>
        <p th:text="${msg2}"></p>
        <!--入力した値をth:object="${formModel}"に詰める  -->
        <!--th:valueで初期値が入るようにする  -->
        <form method="post" th:action="@{/book}" th:object="${formModel}">
            <label>ジャンル:</label>
            <input type="text" name="genre" th:value="*{genre}"><p>
            <label>著者  :</label>
            <input type="text" name="author" th:value="*{author}"><p>
            <label>タイトル:</label>
            <input type="text" name="title" th:value="*{title}"><p>
            <input type="submit" value="検索">
        </form>
        <table>
            <tr>
                <th>ISBN</th>
                <th>タイトル</th>
                <th>著者名</th>
                <th>在庫数</th>
                <th>ジャンル</th>
            </tr>
            <tr th:each="obj:${books}" th:object="${obj}">
                <!--th:hrefでリンク設定  -->
                <td><a th:href="@{/book/detail/{isbn}(isbn=*{isbn})}" th:text="*{isbn}"></a></td>
                <td th:text="*{title}"></td>
                <td th:text="*{author}"></td>
                <td th:text="*{stock}"></td>
                <td th:text="*{genre}"></td>
            </tr>
        </table>
</body>
</html>

処理パターン1

ジャンルに「文学」と入力して、検索ボタン押下。
検索結果画面に遷移すると、前のページで入力した値を保持して、画面に表示。
検索結果のISBNリンクをクリックすると、参照画面に遷移。
image.png

image.png

image.png

処理パターン2

著者に「太」を入力して、検索ボタン押下。
image.png

image.png

最後に

簡易的なアプリを作成してみました。
「もっとこうした方が良い!」などのご意見があればご教示頂ければ幸いです。

参考

Spring Boot 2 プログラミング入門

Spring徹底入門

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

MyBatisで忘れがちなこと個人的なまとめ

MyBatisは比較的自由にSQLが組めて便利なORマッパーなので個人的にはよく使うが、たまに使うと忘れがちなことが色々あるのでその際の備忘録としてまとめてみました。

ヘッダ明細型のデータを扱う

ヘッダテーブルと明細テーブルでが紐づいているデータを1つのマッパーでとってくるときのMyBatisの設定ファイルとSQLの書き方。
MyBatisでヘッダ明細型のデータを扱う

ページネーション用にデータをとってくるときなど行範囲を指定して取得するが、MyBatisのAPIでやろうとすると想定外の行が返ってくる、その回避方法。
MyBatisでヘッダ明細型のデータを扱う(行範囲を指定)

そのほか

他にも順次追記していきます。

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

【AtCoderProblem-ABC100】のC問題「*3 or /2」をJavaで強引に突破する【コード】

はじめに

強引に突破したのでその記録を残しておきます。

問題

問題文が長いのでこちらをどうぞ

コード

TorT.java
import java.util.Scanner;

public class TorT{
  public static void main(String[] args){

    Scanner sc = new Scanner(System.in);

    int N = sc.nextInt();
    int count = 0;

    for(int i = 0; i < N; i++){
      int a = sc.nextInt();
      while(true){
      if(a % 2 == 0){
        a = a / 2;
        count = count + 1;
      }else{
        break;
      }
    }
  }
   System.out.println(count);
  }
}

aという変数が仮に偶数であれば、2で割り切れるまでfor文を回し、その回数をカウントしてあげればOK。奇数であればbreakしてfor文を抜けます。
「3をかける」という事象は「2で割り切れる」のと何ら関係ないので無視するのがいいと思います。

$ java TorT
> 10 
>2184
>2126
>1721
>1800
>1024
>2528
>3360
>1945
>1280
>1776
39

テストケースも全て「AC」で解答することができました。

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

minecraft1.14.4MOD開発覚え書きその1[独自レシピ]

はじめに

minecraft1.14.4のMOD開発を始めたとき、開発に関する情報の少なさに苦労したので、自分用のメモ兼これからMOD開発する方の助けになる(かもしれない)ことを書いておきます。
本記事ではMOD独自のレシピ形式を作るときの解説をします。

開発環境

バージョン
OS Windows10
Forge 28.2.0
JDK AdoptOpenJDK 8u242-b08

環境構築

環境構築を説明した記事は他の方が書いたものがすでにあるので、そちらを参考にしてください。

↓ 私が環境構築する際に参考にした記事です
Minecraft 1.14.4 Forge Modの作成 その1 【開発環境の準備 IntelliJ IDEA】
TNT Modders:環境構築

独自レシピ

例えば二つのアイテムを同時に精錬して合金を得たい、といった通常のレシピでは実現できないことをやりたいときに必要になります。

手順

  1. IRecipeインターフェースを実装する
  2. JSONファイルからレシピを読み取るシリアライザを実装する
  3. シリアライザをForgeに登録する

おおまかな手順はこんな感じです。タイルエンティティなども動作させるには必要ですが、今回はレシピの読み取り・登録をメインに解説します。

実装

ほかに思いつかなかったので、上で書いた例のようなレシピを登録するのを目標とします。

それでは手順に沿って実装していきます。まずはIRecipeインターフェースを実装します。中身はこんな感じ。

IRecipe.class
public interface IRecipe<C extends IInventory> {
    // インベントリ内のアイテムがレシピの材料とマッチするか
    boolean matches(C var1, World var2);

    // クラフトの結果のコピーを返す
    ItemStack getCraftingResult(C var1);

    // これだけよくわからない。基本trueを返す
    boolean canFit(int var1, int var2);

    // クラフトの結果を返す
    ItemStack getRecipeOutput();

    default NonNullList<ItemStack> getRemainingItems(C p_179532_1_) {
        NonNullList<ItemStack> nonnulllist = NonNullList.withSize(p_179532_1_.getSizeInventory(), ItemStack.EMPTY);

        for(int i = 0; i < nonnulllist.size(); ++i) {
            ItemStack item = p_179532_1_.getStackInSlot(i);
            if (item.hasContainerItem()) {
                nonnulllist.set(i, item.getContainerItem());
            }
        }

        return nonnulllist;
    }

    default NonNullList<Ingredient> getIngredients() {
        return NonNullList.create();
    }

    default boolean isDynamic() {
        return false;
    }

    default String getGroup() {
        return "";
    }

    default ItemStack getIcon() {
        return new ItemStack(Blocks.CRAFTING_TABLE);
    }

    // レシピのIDを返す
    ResourceLocation getId();

    // 作成するレシピ形式のJSONファイルを読み書きするシリアライザを返す
    IRecipeSerializer<?> getSerializer();

    // レシピのタイプを返す
    IRecipeType<?> getType();
}

default以外は実装する必要があるので、

  • boolean mathes(C var1, World var2);
  • ItemStack getCraftingResult(C var1);
  • boolean canFit(int var1, int var2);
  • ItemStack getRecipeOutput();
  • ResourceLocation getId();
  • IRecipeSerializer<?> getSerializer();
  • IRecipeType<?> getType();

の7つを実装します。実装するとだいたいこんなコードになります。

ExampleRecipe.java
import com.google.common.collect.Lists;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.IRecipeSerializer;
import net.minecraft.item.crafting.IRecipeType;
import net.minecraft.item.crafting.Ingredient;
import net.minecraft.util.NonNullList;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import net.minecraftforge.common.util.RecipeMatcher;

import java.util.List;

public class ExampleRecipe implements IRecipe<IInventory> {
    public static final IRecipeType<ExampleRecipe> RECIPE_TYPE = new IRecipeType<ExampleRecipe>() {
    };
    protected final ResourceLocation id;
    protected NonNullList<Ingredient> ingredients;
    protected ItemStack result;
    protected int cookTime;

    public ExampleRecipe(ResourceLocation id, NonNullList<Ingredient> ingredients, ItemStack result, int cookTime){
        this.id = id;
        this.ingredients = ingredients;
        this.result = result;
        this.cookTime = cookTime;
    }

    // インベントリ内のアイテムがレシピの材料とマッチするか
    @Override
    public boolean matches(IInventory inventory, World world) {
        List<ItemStack> inputs = Lists.newArrayList();
        for(int index = 0; index < inventory.getSizeInventory(); ++index){
            ItemStack input = inventory.getStackInSlot(index);
            if(!input.isEmpty()){
                inputs.add(input);
            }
        }
        return RecipeMatcher.findMatches(inputs, this.ingredients) != null;
    }

    // クラフトの結果のコピーを返す
    @Override
    public ItemStack getCraftingResult(IInventory inventory) {
        return this.result.copy();
    }

    // これだけよくわからない。基本trueを返す
    @Override
    public boolean canFit(int i, int i1) {
        return true;
    }

    // クラフトの結果を返す
    @Override
    public ItemStack getRecipeOutput() {
        return this.result;
    }

    // レシピのIDを返す(modid:recipe_name)
    @Override
    public ResourceLocation getId() {
        return this.id;
    }

    // レシピのタイプを返す
    @Override
    public IRecipeType<?> getType() {
        return RECIPE_TYPE;
    }
}

matches()やcanFit()以外はレシピの内容を返すゲッター関数なので、難しくはないと思います。

mathes()ではRecipeMatcher.findMatches()を使うことによってインベントリのアイテムがレシピと一致するか確かめています。

canFit()がどのタイミングで使われるのかわかりませんが、バニラのコードを見た限りだとtrueを返す以外のことはやっていませんでした。また、クラフトの結果を返す関数がなぜ二つあるのかもわかっていません。こちらもバニラを参考にして、結果のコピーを返すようにしています。

ちなみにgetSerializer()を実装していないのは、まだ返すシリアライザを実装していないからです。シリアライザを実装し終えたら追加します。

import net.minecraft.item.crafting.IRecipeSerializer;
import net.minecraft.util.JSONUtils;

public static class Serializer extends ForgeRegistryEntry<IRecipeSerializer<?>> implements IRecipeSerializer<ExampleRecipe>{
        public Serializer(){
            this.setRegistryName(new ResourceLocation(modId, "serializer_name"));
        }

        private static NonNullList<Ingredient> readIngredients(JsonArray array){
            NonNullList<Ingredient> ingredients = NonNullList.create();
            for(JsonElement element: array){
                ingredients.add(CraftingHelper.getIngredient(element));
            }
            if(ingredients.isEmpty()){
                throw new JsonParseException("No ingredients for smelting recipe");
            }
            return ingredients;
        }

        @Override
        public ExampleRecipe read(ResourceLocation recipeId, JsonObject jsonObject) {
            ItemStack result = CraftingHelper.getItemStack(JSONUtils.getJsonObject(jsonObject, "result"), true);
            NonNullList<Ingredient> ingredients = readIngredients(JSONUtils.getJsonArray(jsonObject, "ingredients"));
            int cookTime = JSONUtils.getInt(jsonObject, "process_time", 50);
            return new ExampleRecipe(recipeId, ingredients, result, cookTime);
        }

        // サーバーとの通信用
        @Nullable
        @Override
        public ExampleRecipe read(ResourceLocation recipeId, PacketBuffer packetBuffer) {
            final int index = packetBuffer.readVarInt();
            NonNullList<Ingredient> ingredients = NonNullList.withSize(index, Ingredient.EMPTY);
            for(int col = 0; col < index; ++col){
                ingredients.set(col, Ingredient.read(packetBuffer));
            }
            ItemStack result = packetBuffer.readItemStack();
            int cookTime = packetBuffer.readVarInt();
            return new ExampleRecipe(recipeId, ingredients, result, cookTime);
        }

        // サーバーとの通信用
        @Override
        public void write(PacketBuffer packetBuffer, ExampleRecipe exampleRecipe) {
            packetBuffer.writeVarInt(exampleRecipe.ingredients.size());
            for (Ingredient ingredient : exampleRecipe.ingredients) {
                ingredient.write(packetBuffer);
            }
            packetBuffer.writeItemStack(exampleRecipe.result);
            packetBuffer.writeVarInt(exampleRecipe.cookTime);
        }
    }

こちらがシリアライザのコードになります。read()が二つありますが、一つ目はJSONから、二つ目はネットワークパケットから読み込むといった違いがあります。

コンストラクタでは、このシリアライザにIDを振り分けています。このIDがJSONファイルでレシピを定義する際に指定するtypeになります。modIdとserializer_nameは適宜置き換えてください。

readIngredients()はJSONUtilsに複数のIngredientを読み取る関数がないので、自作した関数です。


注意

注意点として、読み取る際の順序が挙げられます。JSONからの読み取りは順序関係がないので気にしなくても問題ないです。しかし、パケットからの読み取りはwrite()で書き込んだのと同じ順序で行わないと、サーバーにログインする際の通信でエラーが発生します。パケットからの読み取りは先頭から順に行われるためです。
オフラインで遊ぶ分には問題ない(JSONからの読み取りしかしないため)ですが、このバグが発生しているサーバーには入れなくなります。これは致命的ですので、順序には細心の注意を払いましょう。


それでは、実装したシリアライザをExampleRecipe.javaにぶち込みたいと思います。

ExampleRecipe.java
import com.google.common.collect.Lists;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.IRecipeSerializer;
import net.minecraft.item.crafting.IRecipeType;
import net.minecraft.item.crafting.Ingredient;
import net.minecraft.network.PacketBuffer;
import net.minecraft.util.JSONUtils;
import net.minecraft.util.NonNullList;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import net.minecraftforge.common.crafting.CraftingHelper;
import net.minecraftforge.common.util.RecipeMatcher;
import net.minecraftforge.registries.ForgeRegistryEntry;

import javax.annotation.Nullable;
import java.util.List;

public class ExampleRecipe implements IRecipe<IInventory> {
    public static final IRecipeType<ExampleRecipe>
    public static final Serializer SERIALIZER = new Serializer();
    RECIPE_TYPE = new IRecipeType<ExampleRecipe>() {
    };
    protected final ResourceLocation id;
    protected NonNullList<Ingredient> ingredients;
    protected ItemStack result;
    protected int cookTime;

    public ExampleRecipe(ResourceLocation id, NonNullList<Ingredient> ingredients, ItemStack result, int cookTime){
        this.id = id;
        this.ingredients = ingredients;
        this.result = result;
        this.cookTime = cookTime;
    }

    // インベントリ内のアイテムがレシピの材料とマッチするか
    @Override
    public boolean matches(IInventory inventory, World world) {
        List<ItemStack> inputs = Lists.newArrayList();
        for(int index = 0; index < inventory.getSizeInventory(); ++index){
            ItemStack input = inventory.getStackInSlot(index);
            if(!input.isEmpty()){
                inputs.add(input);
            }
        }
        return RecipeMatcher.findMatches(inputs, this.ingredients) != null;
    }

    // クラフトの結果のコピーを返す
    @Override
    public ItemStack getCraftingResult(IInventory inventory) {
        return this.result.copy();
    }

    // これだけよくわからない。基本trueを返す
    @Override
    public boolean canFit(int i, int i1) {
        return true;
    }

    // クラフトの結果を返す
    @Override
    public ItemStack getRecipeOutput() {
        return this.result;
    }

    // レシピのIDを返す(modid:recipe_name)
    @Override
    public ResourceLocation getId() {
        return this.id;
    }

    // レシピのタイプを返す
    @Override
    public IRecipeType<?> getType() {
        return RECIPE_TYPE;
    }

    // シリアライザを返す
    @Override
    public IRecipeSerializer<?> getSerializer() {
        return SERIALIZER;
    }

    public static class Serializer extends ForgeRegistryEntry<IRecipeSerializer<?>> implements IRecipeSerializer<ExampleRecipe>{
        public Serializer(){
            this.setRegistryName(new ResourceLocation(modId, "serializer_name"));
        }

        private static NonNullList<Ingredient> readIngredients(JsonArray array){
            NonNullList<Ingredient> ingredients = NonNullList.create();
            for(JsonElement element: array){
                ingredients.add(CraftingHelper.getIngredient(element));
            }
            if(ingredients.isEmpty()){
                throw new JsonParseException("No ingredients for smelting recipe");
            }
            return ingredients;
        }

        @Override
        public ExampleRecipe read(ResourceLocation recipeId, JsonObject jsonObject) {
            ItemStack result = CraftingHelper.getItemStack(JSONUtils.getJsonObject(jsonObject, "result"), true);
            NonNullList<Ingredient> ingredients = readIngredients(JSONUtils.getJsonArray(jsonObject, "ingredients"));
            int cookTime = JSONUtils.getInt(jsonObject, "process_time", 50);
            return new ExampleRecipe(recipeId, ingredients, result, cookTime);
        }

        @Nullable
        @Override
        public ExampleRecipe read(ResourceLocation recipeId, PacketBuffer packetBuffer) {
            final int index = packetBuffer.readVarInt();
            NonNullList<Ingredient> ingredients = NonNullList.withSize(index, Ingredient.EMPTY);
            for(int col = 0; col < index; ++col){
                ingredients.set(col, Ingredient.read(packetBuffer));
            }
            ItemStack result = packetBuffer.readItemStack();
            int cookTime = packetBuffer.readVarInt();
            return new ExampleRecipe(recipeId, ingredients, result, cookTime);
        }

        @Override
        public void write(PacketBuffer packetBuffer, ExampleRecipe exampleRecipe) {
            packetBuffer.writeVarInt(exampleRecipe.ingredients.size());
            for (Ingredient ingredient : exampleRecipe.ingredients) {
                ingredient.write(packetBuffer);
            }
            packetBuffer.writeItemStack(exampleRecipe.result);
            packetBuffer.writeVarInt(exampleRecipe.cookTime);
        }
    }
}

こちらがIRecipeインターフェースを継承して独自レシピを作るコードの最終形になります。ExampleRecipe.javaにgetSerializer()と、変数を追加しました。

あとはシリアライザをForgeに登録して終了です。

RegisterRecipeTypes.java
import net.minecraft.item.crafting.IRecipeSerializer;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;

@Mod.EventBusSubscriber(modid = modId, bus = Mod.EventBusSubscriber.Bus.MOD)
public class RegisterRecipeTypes {
    @SubscribeEvent
    public static void registerRecipeSerializer(RegistryEvent.Register<IRecipeSerializer<?>> event){
        event.getRegistry().register(ExampleRecipe.SERIALIZER);
    }
}

レシピを定義する

最後に今回作ったレシピ形式でレシピを定義します。

example.json
{
  "type": "modId:serializer_name",
  "ingredients": [
    {
       "item": "itemId"
    },
    {
       "item": "itemId"
    }
  ]
  "result": "itemId"
  "process_time": 100
}

おわりに

タイルエンティティの作成については解説するかわからないので、参考になりそうなサイトのリンクを貼って解説を終わりとさせていただきます。

Cadiboo氏が公開しているExampleMod

↑海外の方が作成したMODチュートリアル用のリポジトリです。英語ですがコードの説明が書かれているので、見てみてください。

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

java でAPI呼ぶ時にjavax.net.ssl.SSLHandshakeException発生すること

だれか答えたらありがたいです

https://stackoverflow.com/q/61788988/13538484

       URL url = new URL("https://xxxxRequest");
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json; charset=utf-8 ");
//some headers and params
OutputStreamWriter os = new OutputStreamWriter(conn.getOutputStream());
os.write(json.toString());
os.flush();
conn.connect();
recode = conn.getResponseCode();

エラーメッセージ
javax.net.ssl.SSLHandshakeException: Failed to negotiate the use of secure renegotiation
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1946)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:316)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:306)
at sun.security.ssl.ClientHandshaker.serverHello(ClientHandshaker.java:513)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:207)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1037)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:965)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1064)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1367)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1395)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1379)
at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559)

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

【#FizzBuzz】いろいろな言語で”1から100までの数字を数えて3の倍数と3の付く数字のときにだけアホになる”というのを書いてみる

ふだん定期的にコードを書いていないと、ふとしたときに簡単なロジックのコードも書けなくなってしまう場面に遭遇してしまったため、リハビリの意味も含めて、再勉強した際の内容を自分用のメモとしてまとめました。

0. 説明

世界のナベアツさん(桂三度さん)の最も代表的なネタ「3の倍数と3が付く数字のときだけアホになります」というものを、いろいろな言語で実装してみます。

Wikipedia - 桂三度#主な持ちネタ&ギャグ
https://ja.wikipedia.org/wiki/桂三度#主な持ちネタ&ギャグ

単純に1から100までの数字を改行を入れながら出力するのですが、3の倍数と3が付く数字のときだけ「٩( ᐛ )و」という顔文字を「:(コロン)」と合わせて数字の後ろに出力します。

出力イメージ
1
2
3 : ٩( ᐛ )و
4
5
6 : ٩( ᐛ )و
7
8
9 : ٩( ᐛ )و
10
11
12 : ٩( ᐛ )و
13 : ٩( ᐛ )و
14
15 : ٩( ᐛ )و
16
17
18 : ٩( ᐛ )و
19
20
21 : ٩( ᐛ )و
22
23 : ٩( ᐛ )و
24 : ٩( ᐛ )و
25
26
27 : ٩( ᐛ )و
28
29
30 : ٩( ᐛ )و
31 : ٩( ᐛ )و
32 : ٩( ᐛ )و
33 : ٩( ᐛ )و
34 : ٩( ᐛ )و
35 : ٩( ᐛ )و
36 : ٩( ᐛ )و
37 : ٩( ᐛ )و
38 : ٩( ᐛ )و
39 : ٩( ᐛ )و
40
41
42 : ٩( ᐛ )و
43 : ٩( ᐛ )و
44
45 : ٩( ᐛ )و
46
47
48 : ٩( ᐛ )و
49
50
51 : ٩( ᐛ )و
52
53 : ٩( ᐛ )و
54 : ٩( ᐛ )و
55
56
57 : ٩( ᐛ )و
58
59
60 : ٩( ᐛ )و
61
62
63 : ٩( ᐛ )و
64
65
66 : ٩( ᐛ )و
67
68
69 : ٩( ᐛ )و
70
71
72 : ٩( ᐛ )و
73 : ٩( ᐛ )و
74
75 : ٩( ᐛ )و
76
77
78 : ٩( ᐛ )و
79
80
81 : ٩( ᐛ )و
82
83 : ٩( ᐛ )و
84 : ٩( ᐛ )و
85
86
87 : ٩( ᐛ )و
88
89
90 : ٩( ᐛ )و
91
92
93 : ٩( ᐛ )و
94
95
96 : ٩( ᐛ )و
97
98
99 : ٩( ᐛ )و
100

1. Bashの場合

"3の付く数字"の判定はgrepを使えば良いので意外とシンプルに書けます

Bashの場合
for i in $(seq 1 100) #1から100までの数字をループ
do
  if [ $(($i % 3)) == 0 ] || [ $(echo "$i" | grep "3") ] #3の倍数と3の付く数字のときにだけアホになる 
  then
    echo "${i} : ٩( ᐛ )و"
  else
    echo "$i"  #それ以外は数字の出力のみ
  fi
done

2. Javaの場合

"3の付く数字"の判定はString.valueOf(i).indexOf("3") != -1としました

Javaの場合
import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {
        //1から100までの数字をループ
        for(int i=1; i<=100; i++){
            //3の倍数と3の付く数字のときにだけアホになる 
            if(i%3 == 0 || String.valueOf(i).indexOf("3") != -1 ){ 
                System.out.println(i + " : ٩( ᐛ )و");
            } else {
                System.out.println(i);
            }
        }
    }
}

3. Scalaの場合

いろいろな書き方ができると思いますが、個人的にScalaっぽいと思う書き方にしてみました

Scalaの場合
object Main extends App{
    println(1 to 100 map ((n) => { //1から100までの数字をキーにmapを生成
        var s = n.toString() //mapの値にはキーをそのまま文字列に変換したものを代入
        if(n % 3 == 0 || s.contains('3')) {s += " : ٩( ᐛ )و"} //3の倍数と3の付く数字のときにだけアホになる
        s
    }) reduceLeft ((s,e) => { s + "\n" + e }) ) //mapの値を改行しながら順に出力
}

4. PowerShellの場合

PowerShellの場合もいろいろな書き方ができると思いますが、Scalaにならって関数っぽく書いてみました

PowerShellの場合
1..100 | 
    %{
        if (($_ % 3 -eq 0) -or ($_ -match "3")) { echo("$_" + " : ٩( ᐛ )و") }
        else { echo("$_") }
    }

5. Python3の場合

Pythonはこういう処理が非常に書きやすい言語だと思います

Python3の場合
# coding: utf-8
for num in range(1, 100+1):
    if num % 3 == 0 or "3" in str(num):
        print("{0} : ٩( ᐛ )و".format(num))
    else:
        print(num)

6. Rubyの場合

RubyもPythonと同様にこういう処理が非常に書きやすい言語ですね

Rubyの場合
#!/bin/env ruby
for i in 1..100
  if i % 3 == 0 || i.to_s =~ /3/
    printf( "%d : ٩( ᐛ )و\n", i )
  else
    printf( "%d\n", i )
  end
end

7. Goの場合

Golangは型変換で苦労すると思いきや、意外とシンプルに書く事ができました

Goの場合
package main

import (
    "fmt"
    "strconv"
    "strings"
)

func main() {
    //1から100までの数字をループ
    for i := 1; i <= 100; i++ {
        //intをstringに変換
        var s string = strconv.Itoa(i)
        //3の倍数と3の付く数字のときにだけアホになる 
        if i%3 == 0 || strings.Contains(s, "3") {
            fmt.Println(s + " : ٩( ᐛ )و")
        } else {
            fmt.Println(s)
        }
    }
}

以上です。

気が向いたら他の言語にも挑戦してみようと思います

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

【JavaServlet】千里の道も一歩から 二歩目

jspファイル作りましょう

まずはひな形から

login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ログイン画面</title>
</head>
<body>

</body>
</html>

1行目の<%@ ~ %>はjspファイルを記述するときのお約束で、それ以外は基本的にHTMLのように扱える
ではbody内を実装していくよ

login.jsp
<form action="/ServletApp/mypage" method="post">
name:<input type="text" name="name"><br>
password:<input type="text" name="password"><br>
<input type="submit" value="login">
</form>

以下は起動時の画面※現時点では起動しても何も表示されないよ
servlet5.png
それでは順に解説

タグ名 要素名 説明
form action submit要素をクリックしたときの遷移先
フォルダ構造にもよるが、基本的には
http://localhost:8080/プロジェクト名/action名
method getとpostの2種類から指定
get・・・遷移先のURLの最後に入力したパラメータがくっつく( /action名?name=hoge&password=foo といった感じ)
post・・・くっつかない(今回使用する方)
使い分ける要点は後日記事にする
input type="text" テキストフィールド
name サーバー側で入力データを取得するときのキー
input type="submit" formタグ内のデータをサーバーへ送信するボタン

formのactionに指定するパスですが、
プロジェクト名=ServletApp
遷移させたいjsp=mypage.jsp
の時は/ServletApp/mypageと指定します

サーブレット作りましょう

適当にパッケージを作成したらjavaファイルを新規作成
[スーパークラス]にHttpServletと入力して[参照]をクリック
2020-05-25.png
[一致する項目]からHttpServletを選択して[OK]をクリック
2020-05-25 (2).png
クラスを作成したら、[ソース]-[メソッドのオーバーライド]の順にクリック
servlet6.png
[オーバーライドまたは実装するメソッドを選択]から、
「doGet」、「doPost」、「destroy」、「init(引数なし)」の4つを選択して[OK]をクリック
2020-05-25 (4).png
これでサーブレットで使うメソッドが追加される
メソッドの中身は消してOK
※レスポンス用の引数の命名は基本的に「res」なので、下の画像ように「resp」となっていたら「res」に直した方がいいかも
servlet7.png
まずメソッドから解説

メソッド 説明 用途
doGet jspのformタグのmethodにgetを指定するとこのメソッドが呼びだされる 画面遷移時に実行したい主な処理
doPost 〃にpostを指定すると呼び出されるとこのメソッドが呼び出される
destroy サーブレット破棄時に動く処理 ログ出力処理など
init 最初に動く処理 変数の初期化処理など

doGetとdoPostは利用する方だけを実装しても動くが、method="post"を指定したのにdoGetしか実装してなかった・・・といった事を防ぐため、基本的には両方宣言する
このとき、利用しない方のメソッド内にはdoXXX(req,res)を記述する
そうすると下のような感じになる
servlet8.png
では中身を書いていきましょう
主に使うのは下のやつ

変数 メソッド 説明
res setCharacterEncoding(String charset) jspから受け取ったパラメータを指定した文字コードに変換
addCookie(Cookie cookie) クッキーを設定※後日解説
sendRedirect(String URL) 指定したURLにリダイレクト(遷移)
req getParameter(String key) jspのnameで指定した要素名の値を取得
getSession セッションを取得(後日解説)
setAttribute(String key,Object value) jspに送る要素名と値を設定
getRequestDispacher(String jsppath) 送りたいjspへのファイルパスを設定

具体的な実装内容がこちら
servlet10.png

・18行目 文字コードをUTF-8に設定する
・19、20行目 それぞれ引数にname、passwordを指定することで

login.jsp
name:<input type="text" name="name"><br>
password:<input type="text" name="password"><br>

↑の2つのテキストフィールドから入力値を取得できる

・22行目 パスワードにgoogleを入力していたらグーグルのページへ遷移させる
・24、25行目 次のjspに渡す要素の名前と値を設定する
(25行目のように、もとのjspには無い要素でも関係なく設定できる)
・26行目 次のjspの格納パスを拡張子付きで指定する
最後にforward(req,res)を忘れずにつなげること

遷移先のjsp書きましょう

mypage.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>マイページ</title>
</head>
<body>
<%@ page import="java.util.List" %>
<%@ page import="java.util.ArrayList" %>
<%
    String name=request.getParameter("name");
    List<String> list=new ArrayList<>();
    list.add("Hello!");
%>
<%=name%>さん、<%=list.get(0)%>
</body>
</html>

説明のため、する必要性皆無なコードになっていますが許して
・javaのコードを書きたい ⇒ <% ~ %>で囲う
・変数の値を出力したい ⇒ <%= ~ %>で囲う
・javaのライブラリを使用したい ⇒ <%@ ~ %>で囲う
ちなみに、requestとresponseはライブラリの宣言をしなくても使えます

三歩目へ続く

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