20200305のJavaに関する記事は12件です。

Jackson で Java Enum 列挙型と JSON をシリアライズ・デシリアライズするサンプルコード

概要

  • Java Enum 列挙型と JSON 内の値を相互変換する
  • Jackson の提供する抽象クラス JsonSerializer と JsonDeserializer を使う

動作確認環境

  • Java 11 (AdoptOpenJDK 11.0.6+10)
  • Jackson Databind 2.10.3
  • Gradle 6.2.1
  • macOS Catalina

サンプルコード

package com.example.humansex;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

import java.io.IOException;

/**
 * ISO 5218 (ヒトの性別の表記のためのコード) を表す列挙型です。
 * 性別コード: 不明=0, 男性=1, 女性=2, 適用不能=9
 */
public enum HumanSex {

  /**
   * 不明=0, 男性=1, 女性=2, 適用不能=9
   */
  NOT_KNOWN(0), MALE(1), FEMALE(2), NOT_APPLICABLE(9);

  private final int code;

  private HumanSex(int code) {
    this.code = code;
  }

  /**
   * 性別コードを返します。
   */
  public int getCode() {
    return code;
  }

  /**
   * 性別コードに合致する HumanSex 列挙定数を返します。
   *
   * @param code 性別コード
   * @return HumanSex 列挙定数
   */
  public static HumanSex codeOf(int code) {
    for (HumanSex value : HumanSex.values()) {
      if (code == value.getCode()) {
        return value;
      }
    }
    return null; // 合致せず
  }

  /**
   * HumanSex オブジェクトを JSON にシリアライズするクラスです。
   */
  public static class Serializer extends JsonSerializer<HumanSex> {

    /**
     * JSON 生成時に HumanSex オブジェクトを性別コード(整数値)に変換します。
     */
    @Override
    public void serialize(HumanSex value, JsonGenerator generator, SerializerProvider serializers) throws IOException {
      generator.writeNumber(value.getCode());
    }
  }

  /**
   * JSON から HumanSex オブジェクトをデシリアライズするクラスです。
   */
  public static class Deserializer extends JsonDeserializer<HumanSex> {

    /**
     * JSON 解析時に性別コード(整数値)を HumanSex オブジェクトに変換します。
     */
    @Override
    public HumanSex deserialize(JsonParser parser, DeserializationContext context) throws IOException {
      return HumanSex.codeOf(parser.getIntValue());
    }
  }
}
package com.example;

import com.example.humansex.HumanSex;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;

import java.util.ArrayList;
import java.util.List;

public class MyData {

  public List<Person> personList = new ArrayList<>();

  @Override
  public String toString() {
    StringBuilder buf = new StringBuilder();
    for (Person person : personList) {
      buf.append(person + System.lineSeparator());
    }
    return buf.toString();
  }

  public static class Person {

    public String name;

    // Jackson による JSON 生成・解析時に使うクラスをアノテーションで指定する
    @JsonSerialize(using = HumanSex.Serializer.class)
    @JsonDeserialize(using = HumanSex.Deserializer.class)
    public HumanSex sex;

    public Person() {
    }

    public Person(String name, HumanSex sex) {
      this.name = name;
      this.sex = sex;
    }

    @Override
    public String toString() {
      return "name=[" + name + "], sex=[" + sex.toString() + "]";
    }
  }
}
package com.example;

import com.example.humansex.HumanSex;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;
import java.io.StringWriter;

public class App {

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

    // データオブジェクトを生成
    MyData inputData = new MyData();
    inputData.personList.add(new MyData.Person("Peko",   HumanSex.NOT_KNOWN));
    inputData.personList.add(new MyData.Person("Anubis", HumanSex.MALE));
    inputData.personList.add(new MyData.Person("Isis",   HumanSex.FEMALE));
    inputData.personList.add(new MyData.Person("Robot",  HumanSex.NOT_APPLICABLE));

    // Jackson でデータオブジェクトから JSON 文字列を生成
    StringWriter out = new StringWriter();
    new ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(out, inputData);
    String json = out.toString();

    // JSON 文字列を出力
    System.out.println("***** Mydata → JSON *****");
    System.out.println(json);

    // Jackson で JSON 文字列を解析してデータオブジェクトに変換
    MyData outputData = new ObjectMapper().readValue(json, MyData.class);

    // オブジェクトの文字列表現を出力
    System.out.println("***** JSON → Mydata *****");
    System.out.println(outputData);
  }
}

Gradle 設定ファイル

Gradle でビルドと実行をするための設定ファイル build.gradle を用意。

build.gradle 
plugins {
  id 'java'
  id 'application'
}

group 'org.example'
version '0.0.1'

sourceCompatibility = 11

repositories {
  mavenCentral()
}

dependencies {
  implementation 'com.fasterxml.jackson.core:jackson-databind:2.10.3'
}

application {
  mainClassName = 'com.example.App'
}

実行結果

***** Mydata → JSON *****
{
  "personList" : [ {
    "name" : "Peko",
    "sex" : 0
  }, {
    "name" : "Anubis",
    "sex" : 1
  }, {
    "name" : "Isis",
    "sex" : 2
  }, {
    "name" : "Robot",
    "sex" : 9
  } ]
}
***** JSON → Mydata *****
name=[Peko], sex=[NOT_KNOWN]
name=[Anubis], sex=[MALE]
name=[Isis], sex=[FEMALE]
name=[Robot], sex=[NOT_APPLICABLE]

参考資料

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

【Spring】BeanUtils.copyPropertiesの落とし穴

BeanUtils.copyProperties

SpringにはBeanUtils.copyPropertiesという便利なメソッドが用意されている。
一方のBeanからもう一方のBeanへ、同じ名前のフィールドの中身をコピーしてくれるメソッドだ。第一引数がコピー元、第二引数がコピー先になる。
クラスが別々だったとしても、同じフィールド名があればコピーは可能である。

Book クラス

public class Book {

    String code;

    String name;

    int price;
}

使用例

Book book1 = new Book();

book1.setCode("A");
book1.setName("三国志");
book1.setPrice(500);

Book book2 = new Book();

BeanUtils.copyProperties(book1, book2);

こうすることで、book2にはbook1codenamepriceの値が丸々コピーされる。

落とし穴1: 異なるクラスのオブジェクトにコピーする場合にフィールド名が変わると死ぬ

例えば以下のFormとDtoがあるとしよう。

CreateBookForm クラス

public class CreateBookForm {

    String code;

    String name;

    int price;
}

BookDto クラス

public class BookDto {

    String code;

    String name;

    int price;

    String updateModule;

}

Form -> Dtoのマッピングに無邪気にBeanUtils.copyProperties(createBookForm, bookDto)などと書くと、死亡率が高まる。

フロントエンドの都合でCreateBookFormのフィールド名がcodecdと変わったとする。

CreateBookForm クラス(変更後)

public class CreateBookForm {

    String cd;

    String name;

    int price;
}

もうこれでcd -> codeのコピーは実施されない。フィールド名が異なるからだ。
じゃあBookDtoのフィールド名も変えればいいじゃん!って意見もあるかもだが、レイヤー分離のために用意しているクラスで変更が影響し合うのはイケてない。

このメソッドを使う場合、以下の原則を守るべきだと自分は考える。

  • 異なるクラスのオブジェクト同士ではコピーさせない!フィールド名が永久に変更されないという保証はどこにもない!ただし継承関係のあるクラスは除く。
  • どうしても異なるクラスのオブジェクト同士でコピーさせる必要がある場合は、コピーされることを確認する単体テストを書く!

落とし穴2: 同姓同名のメソッドが別のライブラリに存在する

このメソッド、全く同じ名前のものがApache Commonsライブラリに存在するのである。
しかもこちらのメソッド、第二引数がコピー元、第一引数がコピー先となっている。Springの同名メソッドと引数の順番が逆なのである!!
IDEでライブラリ自動インポートを使っていると、あるクラスではSpringの、あるクラスではApacheのcopyPropertiesを呼んでしまう場合があり、気づかずハマることがあるので注意。
自分はハマった。

個人的には「コピー元 -> コピー先」で「左から右へ = 第一引数から第二引数へ」の印象が強いので、Springのものを常に使うようにしている。

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

初心者レベルでも気づく、JavaとGoのswitch文の記述の違い

基本的なswitch文の記法

Java
switch rank {
    case 1:
        System.out.println("金メダル!");
        break;
    case 2:
        System.out.println("銀メダル!");
        break;
    case 3:
        System.out.println("銅メダル!");
        break;
    default:
        System.out.println("メダルを獲得できませんでした");
        break;
}
Go
switch rank {
case 1:
    println("金メダル!")
case 2:
    println("銀メダル!")
case 3:
    println("銅メダル!")
default:
    println("メダルを獲得できませんでした")
}

Javaのswitch文の場合、暗黙的にfall-throughであるため、fall-throughされたくない場合はbreak文を明示的に書く必要があります。

一方、Goのswitch文は、暗黙的にfall-throughでなく、明示的なbreak文は必要ありません。

複数条件のラベルを含むswitch

Java
switch n {
    case 0:
        System.out.println("凶です");
        break;
    case 1:
    case 2:
        System.out.println("吉です");
        break;
    case 3:
    case 4:
        System.out.println("中吉です");
        break;
    case 5:
        System.out.println("大吉です");
        break;
}
Go
switch n {
case 0:
    println("凶です")
case 1, 2:
    println("吉です")
case 3, 4:
    println("中吉です")
case 5:
    println("大吉です")
}

Goのswitch文では、caseにおいてカンマ区切りで複数条件を一度に指定することができます。

一方(JDK11までの)Javaのswitch文では、「カンマ区切りで複数条件を一度に指定する」という記法は使えません。上記ソースコードのように、fall-throughを使って書く必要があります。

結論

同じC-like languagesであっても、特にswitch文の記法については、言語によって少なからぬ差が出ますね。

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

未経験エンジニアが覚えておきたい用語

概要

IT業界に飛び込んで約半年、いろいろな技術を勉強してきました。
ただ、これやったけどなんだっけという事が多くなったので、備忘録として基本的なことを書いていきます。

JAVA

・オーバーライド
 →スーパークラスで定義されているメソッドをサブクラスで上書きする
・オーバーロード
 →引数や戻り値が異なるが名称が同一のメソッドを定義する
・ポリモフィズム
 →同じ操作でも扱う対象によって振る舞いが異なる仕組み
・継承
 →あるクラスを基に新しいクラスを定義する
・オブジェクト
 →クラスを複数定義しクラスから生成したオブジェクトを利用。
 【特徴】 再利用性が高い、仕様変更に強い
・抽象クラス
 →抽象メソッド1つ以上定義したクラス 
 【特徴】 abstractを指定、メソッドを定義できる、インスタンス化出来ない、実装は持たない
・カプセル化
 →publicやprivateなどのアクセス修飾子を適切に設定し、クラスに対するアクセス制限を行う
 【特徴】 クラスのアクセスが統一されるため、独立性が高まる、バグを未然に防げる、クラスの変更が簡単
・ArrayListとLinkedListの違い
 →ArrayListはアクセスに強い、LinkedListは追加や削除に強い
 【理由】 
 ①ArrayListはListインターフェイスを実装しており、配列の要素番号があるため検索が容易にできる。
 ②LinkedListは要素同士が前後に持っているリンク情報で数珠状態でつながっているため情報の追加などは容易にできる。

DB周り

・SQL内部結合と外部結合の違い
 →inser joinは条件が一致していて、テーブルに値があるものを持ってくるが、outer join は値が一方のテーブルにしかなくても取得ができる。
・インデックス
 →インデックスを使うとデータの参照が高速になる
・シーケンス
 →データを一位に特定する列を作る時に使う(ID・学生番号などの重複させたくない時)

WEBアプリケーションの脆弱性

・アプリケーション・バッファーオーバーフロー
 →大量のファイルを送りつけてシステムダウンを起こす
・XSS
 →ユーザがwebページにアクセスすることで不正なスクリプトが実行されてしまう。その結果不正ログインなどが実行される。
・CSRF クロスサイトフォージェリー
 →webアプリケーション利用者が意図しない処理が実行されてしまう。意図しないWEBアプリケーション上の処理実行。

そのほかいろいろ

・バッチ処理
 →複数のプログラムからなる作業を一連の手順を登録しておいてまとめて連続的に実行すること。集計処理とかバッチ処理!
・デプロイ
 →特定の環境下でアプリケーションやシステムを使えるようにすること。

最後に

ほんとに初期の部分をまとめてみました。
言葉はわかるけど説明してっていわれたら出来なさそうとか思う用語がちょいちょいありました。
根拠を持って実装ができるといいね!

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

@Builder(Lombok)の使い方

Lombokの@Builderアノテーションとは?

  • JavaのClassに@Builderを付与することで、Builderメソッドを生成してくれる。
  • 引数のながーいコンストラクターを用意しなくてすむ。
import lombok.Builder;

//呼び出される側のJavaClass
@Builder
public class Employee {
    private String name;
    private int syainId;
    private int age;
    private String position;
    private String status;
}
//Builderを呼び出す側
Employee employee = Employee.builder()
        .name("Kohei Sato")
        .syainId(101)
        .age(32)
        .position("developper")
        .build();

初期値は??

上記の例では、statusを指定していない。→ statusはnullになってしまう。
Stringなのでnullになっているが、intは0, booleanはfalseになるっぽい。

初期値の指定方法1:内部クラス

  • 以下の通り,ClassName+Builderのクラスで、デフォルト値を指定可能
@Builder
public class Employee {
    private String name;
    private int syainId;
    private int age;
    private String position;
    private String status;

    public static class EmployeeBuilder {
        private String status = "Active";
    }
}

初期値の指定方法2:Defaultアノテーションを利用

  • アノテーション付与で変数宣言時に初期値を指定可能。
@Builder
public class Employee {
    @Builder.Default private String name = "No Name";
    @Builder.Default private int syainId = 0;
    @Builder.Default private int age = 30;
    @Builder.Default private String position = "Normal";
    @Builder.Default private String status = "Active";
}

こっちの方が個人的に見やすい。

参考

https://reinhard.codes/2016/07/13/using-lomboks-builder-annotation-with-default-values/

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

Android studio でのメモ

Android studio

・テキストの編集
編集したいテキストをクリックし、画面右側のattributes内の項目から編集する。

java

・値等を扱う場合、値の種類を明確にする必要があり、型に入れる必要がある。

型の名前 サイズ 値の範囲
byte 1 バイト -128 ~ 127
short 2 バイト -32768 ~ 32767
int 4 バイト -2147483648 ~ 2147483647
long 8 バイト -9223372036854775808 ~ 9223372036854775807

入れる値が小さいからといって、積極的に byte や short を使う必要はない。
普通の整数には int、非常に大きな数になることがある場合には long、という程度の使い分けでいい。
更に、小数を扱う場合には、浮動小数点数を表す型を使う必要がある。

型の名前 サイズ
float 4 バイト
double 8 バイト

float と double どちらも同じように使えるが、 float よりも double の方が、より巨大な数、より細かい数を表現することができる。

int x = 0.3; // これはエラー
double y = 0.3; //
このように float や double を使うこと
真偽値を表現するためには boolean 型を使う。

boolean flag = false;
文字列には String 型を使う。S は大文字なので注意。

また、文字列をソースコード中に記述するときには、ダブルクオーテーション " を使わなければならない。

String message = "Hello";

・演算子

演算子
+ 足し算
- 引き算
* 掛け算
/ 割り算
% 割り算の余り
&& 論理積(かつ)
II 論理和(または)
! 否定(ではない)
< より小さい(未満)
> より大きい(超過)
<= 以下
>= 以上
== 等しい
!= 等しくない
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SSHJ使用時のSLF4Jに関する警告メッセージを抑止する

やりたいこと

SSHJを使用した時に出力される警告メッセージを抑止することです。

問題概要

SSHJのSSHClientクラスをnewした時に警告メッセージが出力されます。

問題詳細

SSHJのSSHClientクラスをnewした時に以下の警告メッセージが出力されます。

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.

警告が出力されても、SSHJは問題なく使用できます。
たぶん、ログ出力ができないだけなのではないかと思います。

解決策

まずは素直に、警告メッセージに示されているサイトを参照します。
すると、

  • "org.slf4j.impl.StaticLoggerBinder"クラスがメモリにロードできなかった時に出力されるメッセージであること
  • クラスパスに、slf4j-nop.jar、slf4j-simple.jar、slf4j-log4j12.jar、slf4j-jdk14.jar、または、logback-classic.jarのいずれか1つを配置することで問題が解決すること

が書かれています。
ということで、実行環境のクラスパスに、slf4j-nop-x.x.x.jarを配置することで警告メッセージを抑止することができました。
x.x.xの部分はバージョン番号になります。

ちなみに、EclipseのMavenプロジェクトをコンパイル/デバッグするような場合はpom.xmlに以下のように記述しておけば良さそうです。

pom.xml
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-nop -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-nop</artifactId>
    <version>1.7.7</version>
</dependency>

なお、上の例でバージョンに1.7.7を指定しているのは、SSHJがslf4j-api1.7.7を必要としているようだったので、そのバージョンと合わせてみました。

-- 以上--

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

Gsonを使ってみた

Gsonとは

そのまま引用します。

Gson
Gson is a Java library that can be used to convert Java Objects into their JSON >representation. It can also be used to convert a JSON string to an equivalent Java object. >Gson can work with arbitrary Java objects including pre-existing objects that you do not have >source-code of.

There are a few open-source projects that can convert Java objects to JSON. However, most of >them require that you place Java annotations in your classes; something that you can not do if >you do not have access to the source-code. Most also do not fully support the use of Java >Generics. Gson considers both of these as very important design goals.

実験に使用するサンプル

Person1.java
import java.util.Date;
public class Person1 {

    private Integer age;
    private String name;
    private String noUse;
    private Date target;

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String noUse() {
        return noUse;
    }

    public void noUse(String noUse) {
        this.noUse = noUse;
    }

    public Date target() {
        return target;
    }

    public void target(Date target) {
        this.target = target;
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("Person1 [age=");
        builder.append(age);
        builder.append(", name=");
        builder.append(name);
        builder.append(", noUse=");
        builder.append(noUse);
        builder.append(", target=");
        builder.append(target);        
        builder.append("]");
        return builder.toString();
    }

}

まずは独自の型に変換する

Ex1.java
public class Sample {
    public static void main(String[] args){
        //-----------------------------
        // jsonReaderの実験
        //-----------------------------
        StringBuilder jsonReader = new StringBuilder();
        jsonReader.append("[");
        jsonReader.append(" {\"age\":1,\"name\":\"Gson Taro\",\"target\":\"2020-02-10\"},");
        jsonReader.append(" {\"age\":2,\"name\":\"Gson Taro\",\"target\":\"2020-02-30\"},");
        jsonReader.append(" {\"age\":3,\"name\":\"Gson Taro\",\"target\":\"2020-02-12\"}");
        jsonReader.append("]");

        try {
            Gson gsonReader = new Gson();
            Type listType = new TypeToken<List<Person1>>(){}.getType();
            List<Person1> p1 = gsonReader.fromJson(jsonReader.toString(), listType);

            System.out.println("------------------------------------------->");
            System.out.println("Person1#toString: " + p1);
            System.out.println("------------------------------------------->");
        } catch (Exception e) {
            System.out.println(e.getCause());
            System.out.println("------------------------------------------->");
            if(e.getCause() instanceof NumberFormatException) {
                System.out.println("数値エラー:" + e.getCause().getMessage());
            }else if (e.getCause() instanceof ParseException){ 
                System.out.println("日付入力エラー:" + e.getCause().getMessage());
            }
            System.out.println("------------------------------------------->");
        }
    }

【成功】

難なく成功です。Gsonに変換してほしい型を渡す部分が肝です。

------------------------------------------->
Person1#toString: [Person1 [age=1, name=Gson Taro, noUse=null, target=Mon Feb 10 00:00:00 JST 2020], Person1 [age=2, name=Gson Taro, noUse=null, target=Sun Mar 01 00:00:00 JST 2020], Person1 [age=3, name=Gson Taro, noUse=null, target=Wed Feb 12 00:00:00 JST 2020]]
------------------------------------------->

次にInteger型へnullを割り当てる実験

Integer型にしている項目をnullにする

test.java
        StringBuilder jsonReader = new StringBuilder();
        jsonReader.append("[");
        jsonReader.append(" {\"age\":null,\"name\":\"Gson Taro\",\"target\":\"2020-02-10\"},");
        jsonReader.append(" {\"age\":2,\"name\":\"Gson Taro\",\"target\":\"2020-02-30\"},");
        jsonReader.append(" {\"age\":3,\"name\":\"Gson Taro\",\"target\":\"2020-02-12\"}");
        jsonReader.append("]");

【成功】

ちなみに受け取る方がint型の場合は「0」になりました

------------------------------------------->
Person1#toString: [Person1 [age=null, name=Gson Taro, noUse=null, target=Mon Feb 10 00:00:00 JST 2020], Person1 [age=2, name=Gson Taro, noUse=null, target=Sun Mar 01 00:00:00 JST 2020], Person1 [age=3, name=Gson Taro, noUse=null, target=Wed Feb 12 00:00:00 JST 2020]]
------------------------------------------->

次にInteger型へ文字列を割り当てる実験

test.java
        StringBuilder jsonReader = new StringBuilder();
        jsonReader.append("[");
        jsonReader.append(" {\"age\":test,\"name\":\"Gson Taro\",\"target\":\"2020-02-10\"},");
        jsonReader.append(" {\"age\":2,\"name\":\"Gson Taro\",\"target\":\"2020-02-30\"},");
        jsonReader.append(" {\"age\":3,\"name\":\"Gson Taro\",\"target\":\"2020-02-12\"}");
        jsonReader.append("]");

【失敗】

NumberFormatExceptionが発生します。

次はDate型に変換できる形式をチェックします

test.java
        jsonReader.append("[");
        jsonReader.append(" {\"age\":1,\"name\":\"Gson Taro\",\"target\":\"2020-02-10\"},");
        jsonReader.append(" {\"age\":2,\"name\":\"Gson Taro\",\"target\":\"20200230\"},");
        jsonReader.append(" {\"age\":3,\"name\":\"Gson Taro\",\"target\":\"2020/02/12\"}");
        jsonReader.append("]");

【失敗】

Gsonはjson上で上の行、左の項目からパースして、
変換に失敗した段階で例外を出します。
今回の実験により「yyyy-MM-dd」「yyyyMMdd」の形式の場合は日付に変換されますが、
「yyyy/MM/dd」の場合は例外が発生します。

------------------------------------------->
日付入力エラー:Failed to parse date ["2020/02/12"]: Invalid number: _0
------------------------------------------->

日付の成功例の実験

test.java
        StringBuilder jsonReader = new StringBuilder();
        jsonReader.append("[");
        jsonReader.append(" {\"age\":1,\"name\":\"Gson Taro\",\"target\":\"2020-02-10\"},");
        jsonReader.append(" {\"age\":2,\"name\":\"Gson Taro\",\"target\":\"20200230\"},");
        jsonReader.append(" {\"age\":3,\"name\":\"Gson Taro\",\"target\":\"20200212\"}");
        jsonReader.append("]");

結果はこちら

------------------------------------------->
Person1#toString: [Person1 [age=1, name=Gson Taro, noUse=null, target=Mon Feb 10 00:00:00 JST 2020], Person1 [age=2, name=Gson Taro, noUse=null, target=Sun Mar 01 00:00:00 JST 2020], Person1 [age=3, name=Gson Taro, noUse=null, target=Wed Feb 12 00:00:00 JST 2020]]
------------------------------------------->

ここで、面白いことに2020/2/30が2020/3/1に変換されています。

この部分Gsonのソースを読んでみます。

Dateに変換しているのはこの部分っぽいですね。
ここはなんとかカスタマイズできそう。。。

ということで調べてみると、この部分で日付のフォーマットを指定できそう。
使ってみます。

test.java
        jsonReader.append("[");
        jsonReader.append(" {\"age\":1,\"name\":\"Gson Taro\",\"target\":\"2020-02-10\"},");
        jsonReader.append(" {\"age\":2,\"name\":\"Gson Taro\",\"target\":\"20200230\"},");
        jsonReader.append(" {\"age\":3,\"name\":\"Gson Taro\",\"target\":\"2020/02/12\"}");
        jsonReader.append("]");
        Gson gsonReader = new GsonBuilder().setDateFormat("yyyy/MM/dd").create();
        Type listType = new TypeToken<List<Person1>>(){}.getType();
        List<Person1> p1 = gsonReader.fromJson(jsonReader.toString(), listType);

        System.out.println("------------------------------------------->");
        System.out.println("Person1#toString: " + p1);
        System.out.println("------------------------------------------->");

結果はパース成功です!

------------------------------------------->
Person1#toString: [Person1 [age=1, name=Gson Taro, noUse=null, target=Mon Feb 10 00:00:00 JST 2020], Person1 [age=2, name=Gson Taro, noUse=null, target=Sun Mar 01 00:00:00 JST 2020], Person1 [age=3, name=Gson Taro, noUse=null, target=Wed Feb 12 00:00:00 JST 2020]]
------------------------------------------->

いいですね。非常に使いやすい!!

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

Java 温故而知新 不亦乐乎

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

初心者から始めるJava、if文・switch文

はじめに

この記事は備忘録である。
参考書レベルの内容だが、本記事に掲載するコードについては、
間違えたものが中心となる。これは実際にコーディング中に間違えた部分を掲載し、自分で反省するために投稿するという目的によるもの。
また、後日にJavaSilver試験問題の勉強を兼ねて復習するため、深い部分の話はここでは触れない。

環境

言語:Java11、JDK13.0.2
動作環境:Windows10

条件判断文

状況に応じた処理を行う場合、Javaでは「条件(Condition)」という考え方で処理の前提をとらえることになる。日常生活で使う条件と同じと考えていいが、Javaにおける「条件」は、TrueかFalseどちらかの値を持つものを指す。
条件判断文は、「条件」の値に応じて処理を行う文であり、条件の値を境に処理の内容を分けて用意することができる機能である。


  • if文
  • もし~であるならば(でないのなら)、〇〇する。
    そうでないならば(else)、△△する。

  • switch文
  • もし、〇〇の値が●●ならば、◎◎する。
    △△の値が▲▲ならば、▼▼する。
    いずれでもないのなら(default)、□□する。

関係演算子

関係演算子のうち、等号と不等号の話は前回した。残りの関係演算子は数学で扱ったものとほぼ変わらない。

  • ==
  • 左辺右辺が等しい
  • !=
  • 左辺右辺が異なる
  • >
  • 左辺のほうが右辺よりも大きい
  • >=
  • 左辺のほうが右辺以上に大きい
  • <
  • 右辺のほうが左辺よりも大きい
  • <=
  • 右辺のほうが左辺以上に大きい

if文

基本構造はこう。

if(条件){
  内容;
  内容;
  ...
}
else{
  内容;
  内容;
  ...
}

;(セミコロン)の位置にはくれぐれも注意。ifやelseの直後に付かないし、内容の後には必ず付ける。また、特段の事情がない限りは、{}でifとelseそれぞれの内容を囲っておくこと。

以下間違い。

WrongEnclosing.java
...
int eggsInPoundCake = 2;
int theseEggsWeUse = 8;

if(theseEggsWeUse != eggsInPoundCake)
  System.out.println("パウンドケーキに必要な卵の数は2個です。");
  System.out.println("パウンドケーキのために卵を" + eggsInPoundCake +"個貰いますね。");
  theseEggsWeUse= theseEggsWeUse - eggsInPoundCake;
else
  System.out.println("卵をすべて使い切ってしまいました。");

 //2か所も間違っている。でもコンパイルできない理由はひとつだ。

{}で囲っていないため、if文は

 System.out.println("パウンドケーキに必要な卵の数は2個です。");
の段までしか掛かっていない。下のelse文が接続していない扱いとしてコンパイルエラーが出る。
もしelseがない場合、

 System.out.println("パウンドケーキのために卵を" + eggsInPoundCake +"個貰いますね。");
theseEggsWeUse= theseEggsWeUse - eggsInPoundCake;

この2行はif文の外として扱われ、条件分けした(と思い込んでいる)にも関わらず勝手に処理される。

else if については今回は割愛。

switch文

基本構造はこう。

switch(式){
  case 値:
   内容;
   ...
   break;
  case 値:
   内容;
   ...
   break;
  default:
   内容;
   ...
   break;
}

breakとは、ブロック内の処理を強制的に終わらせてブロック外に抜ける文である。switch文では、(defaultがあれば)defaultの内容まで該当する処理の中身すべてを表示してしまうため、処理の流れを操作したい場合にbreakが使われる。
以下間違え。

noBreak.java
...
int creditInSchool = 0;
char judge = 'A';
... //学校の単位に必要な各条件の検討
    //judgeが単位

//仮に彼はAの単位のままだったとする
switch(judge){
  case 'A':
    System.out.println("彼の単位は" + judge + "だ。");
  case 'B':
    System.out.println("彼の単位は" + judge + "だ。");
  case 'C':
    System.out.println("彼の単位は" + judge + "だ。");
  case 'D':
    System.out.println("彼の単位は" + judge + "だ。");
  default :
    System.out.println("彼の単位は" + judge + "、この単位は上げられないよ");
}

//結果
//彼の単位はAだ。
//彼の単位はAだ。
//彼の単位はAだ。
//彼の単位はAだ。
//彼の単位はA、この単位は上げられないよ

breakを入れないと、すべてのケースが実行されてしまう。

終わりに

条件文は、実例かあるいは設問を数解くことが上達への道だろう。else ifが山積みの問題は難しいし、何より実践でごちゃごちゃにするとスパゲッティコード間違いなし。

参考

出来るだけ自分で変数や式を書いてコンパイルしているので、完全に引用する場合はその旨記述する。

やさしいJava 第7版

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

React Native vs. Ionic – A head-to-head Comparison in 2020

In this article, we are going to look at an intense comparison between Ionic and React Native development frameworks. At the end of this article, you will be all clear about what framework you should use for your development projects.

click here to read morehttps://www.positronx.io/react-native-vs-ionic-head-to-head-comparison/

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

Javaのコレクション入門

こちらのブログ記事をQiitaに投稿したものです。

0.前置き

Javaに限らず、プログラミングではデータをまとめておくのに配列だけではどうにもうまくいかないことが多々あります。そこでデータ構造が重要になってくるのですが、そのあたりの表現方法は言語によって様々です。

C++ならvectorとかlistとか標準ライブラリのコンテナが用意されていて、(あまり経験ないけど)C#だとLINQというものを使うようです。PHPだと配列でかなりのことが出来たりします(その分使い方には慎重さを要する)。
Pythonだとリスト型やタプル、辞書などを用途に応じて使い分ける感じですね。

1.コレクションフレームワークとは

さて、Java。Javaでは、コレクションフレームワークというものを使います。コレクションフレームワークではリスト(値が順番に並んだもの)、セット(値が順番になっているとは限らないが、同じ値のものが1つだけのもの)、マップ(キーごとに値が対応したもの)といったデータを表現するのに必要なデータ構造が一通り揃っています(他にも両端からしか値を出し入れできないDequeというのもあるんですが、用途が限られるので今回は割愛)。

2. リスト

まずはリスト。リストは0から始まるインデックスごとにデータが入ったものです。Listインターフェースを元に実装されていて、ArrayList(可変長配列)とLinkedList(連結リスト)の2つのクラスがよく使われます。
ArrayListがC++でいうvectorみたいなものです。なお、JavaにもVectorクラスはあるんですが、こちらはJava8以降では非推奨になっています。
それぞれ、次のように定義します。

List<Integer> arrayList = new ArrayList<>();
List<Integer> linkedList = new LinkedList<>();

どちらのクラスで定義するかは、コンストラクタを呼び出す時に決まります。ちなみに、クラスとかっこの間の<>は、ダイヤモンド演算子といって、要素の型を省略する時の記法です。
よく使う操作は以下のような感じです。ArrayListに対しての記述になっていますが、LinkedListであっても行う処理は同じです。

List<String> arrayList = new ArrayList<>();
List<String> linkedList = new LinkedList<>();
arrayList.add("Apple");  // リストの最後に要素"Apple"を追加
arrayList.get(5);  // リストの5 + 1番目の要素を取得する
arrayList.set(3, "Orange");  // リストの3 + 1番目の要素の値を"Orange"に設定する
arrayList.remove(4); // リストの4 + 1番目の要素を削除し、後続のインデックスを1つ減らす
arrayList.size();  // リストの要素数を返す
arrayList.clear();  // リストを空にする
// arrayListの要素一覧を出力する
for (String data : arrayList) {
    System.out.println(data);
}

ただ、上で「行う処理は同じ」と書きましたが、ArrayListとLinkedListでは実装方法が違うので、それぞれ得意不得意があったりします。
getとかsetとかはそれぞれの要素がメモリ上でインデックスされているArrayListの方が速く、addとかremoveは要素間がお互いのリンク情報(C言語のポインタのようなイメージ。Javaにはポインタはないけど)で繋がっているLinkedListの方が高速だったりします。
なので、データベースとかからある程度大きなデータを持ってきて、その中の要素へのアクセスが主目的の場合はArrayListが、リストへのデータの追加や削除が頻繁に発生する場合はLinkedListが向いています。

3. セット

セットはリストと同様にデータをまとめたものですが、リストとは違ってインデックスは存在せず、また、同じ値のものは2つ以上持てないというものです。大学で数学を勉強した人は集合・位相の集合をイメージしてもらえればと思います。
こちらはSetインターフェースを元に実装されていて、HashSet、TreeSet、LinkedHashSetの3つがよく使われます。それぞれ、次のように定義します。

Set<String> hashSet = new HashSet<>();
Set<String> treeList = new TreeList<>();
Set<String> linkedHashSet = new LinkedHashSet<>();

基本的な操作は以下のような感じです。HashSetに対して書いていますが、他の2つでも同じです。getとsetがないところに気をつけてください。

Set<String> hashSet = new HashSet<>();
Set<String> treeSet = new TreeSet<>();
Set<String> linkedHashSet = new LinkedHashSet<>();
hashSet.add("Banana");  // 集合の要素に"Banana"が含まれていなければ追加する
hashSet.remove("Lemon"); // 集合に"Lemon"が含まれていたら削除する
hashSet.size();  // 集合の要素数を返す
// hashSetの要素一覧を出力する
for (String data : hashSet) {
    System.out.println(data);
}

HashSet、TreeSet、LinkedHashSetの違いですが、HashSetは処理が高速な代わりに順番が全く保証されず、TreeSetは要素がソートされた状態で保持され、LinkedHashSetは要素を追加した順番が保持されます。

treeSet.add("Banana");
treeSet.add("Orange");
treeSet.add("Apple");
for (String data : treeSet) {
    System.out.println(data);
}

だと、”Apple” -> “Banana” -> “Orange”の順に出力され、

linkedHashSet.add("Banana"); 
linkedHashSet.add("Orange");
linkedHashSet.add("Apple"); 
for (String data : linkedHashSet) {
    System.out.println(data);
}

だと、”Banana” -> “Orange” -> “Apple”の順番に出力されます。

4. マップ

マップは値だけでなく、値を指定するキーと一緒の組み合わせでデータを保持するものです。マップではキーは一意(同じものが複数存在しない)ですが、値は同じものが複数あってもいいです。
マップはMapインターフェースを元に実装されていて、HashMap、TreMap、LinkedHashMapの3つがよく使われます。それぞれ次のように定義します。

Map<String, Integer> hashMap = new HashMap<>();
Map<String, Integer> treeMap = new TreeMap<>();
Map<String, Integer> linkedHashMap = new LinkedHashMap<>();

基本的な操作は以下のようになります。putメソッドを使ってキーと値の組合せをマップに追加します。

Map<String, Integer> hashMap = new HashMap<>();
Map<String, Integer> treeMap = new TreeMap<>();
Map<String, Integer> linkedHashMap = new LinkedHashMap<>();
hashMap.put("Tomato", 300);  // マップのキー"Tomato"の値を300に設定する
hashMap.get("Tomato");  // マップのキーが"Tomato"のものの値を返す(なければnull)
hashMap.remove("Lemon"); // マップにキーが"Lemon"のものが含まれていたら削除する
hashMap.size();  // マップの要素数を返す
// マップの要素ごとにキー:値という形で出力する
for (Map.Entry<String, Integer> data : hashSet.entrySet()) {
    System.out.println(data.getKey() + ":" + data.getValue());
}

データを全て出力するのが他より若干手間がかかりますね。
こちらも、セットと同様HashMapは高速だけど順番が保証されない、TreeMapはキーを順番としてデータを保持、LinkedHashMapはデータを追加した順番に保持されます。

5.終わりに

Javaで通常よく使うコレクションについて、駆け足で紹介してみました。他にもいくつかあるようですが、今回紹介したものを用途に応じて使いこなせれば、Javaでのデータの扱いで困ることはそんなにはないと思います。
自分は以前、名前とあるオブジェクトの組み合わせのデータをHashMapで定義していたのですが、途中で名前の昇順に出力する必要があることに気がついて、あわててTreeMapに書き直したことがありました。皆さんもコレクションを使うときは何を目的にしているかを考えて、最適なものを選択するようにしてください。

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