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

結局for文を使うのか?while文を使うのか?for文とwhile文の使い分け

※この記事はCやJavaなどCライクな構文のfor文を持つ言語向けの記事です。Python等foreachな言語は対象外です。

今更のようですが、for文とwhile文の使い分け、プログラミングを始めたての頃には誰しも疑問に思ったことでしょう。

世の中の一般的な文献には、for文は繰り返し回数が明らかなときに使用するなどと書かれていることが多いようです。

しかしながら、自分としてはこの説明があまり腑に落ちませんでした。

今日プログラムを書いていて、この違いについてやっと自分の中で一つの落としどころが見つかったので、ここに記録しておこうと思います。

以下、JavaScriptで記述します。

class Iterator {
  index = 0;
  constructor(list) {
    this.list = list;
  }
  next() {
    return this.list[this.index++];
  }
}

const iterator = new Iterator([1, 2, 3, 4, 5]);

以上のような実装のIteratorクラスを用意します(実際にはファイルリストなどをイメージしてもらえれば良いです)。ここで、すべての要素を出力しつつ、要素の個数を数える処理を記述したいとします。当初、私は次のようなコードを書きました1

let count = 0, item;
while (item = iterator.next()) {
  console.log(item);
  count++;
}
console.log(`個数: ${count}`);

コードをより読みやすくするため、以下のように書き換えてみます。

let count, item;
for (count = 0; item = iterator.next(); count++) {
  console.log(item);
}
console.log(`個数: ${count}`);

書き換えてみたのですが・・・このコード、確かに短くはなっているのですが、非常に読み辛いと感じませんか?

恐らくこの理由は、私たちが

for (A; B; C) {}

という文を見たとき、無意識に

For A, while B, do C.

つまりAについて、Bの間、Cしなさいと読んでいるためだと思うのです。このため、実際の式をここに当てはめたとき、文章として成り立たないと強い違和感を覚えます。

もう少し踏み込んだ表現をするのであれば、意識する必要があるのはAの文、つまり初期設定式です。「for」という単語(=~について)が使われている以上、BやC、そしてブロック内の文は嫌でもこのAの部分に束縛を受けます。

先ほどのfor文の例に違和感があったのは、初期設定式は変数countの初期化に使用されているにもかかわらず、継続条件式やブロック内の文がcountと全く関係のないものだったからなのです。

まとめ

while文で表現可能な繰り返しのうち、for文で表現するべきものは

  • 繰り返し初期に実施すべき式が存在する
  • その式の主たる対象が継続条件や再設定式、ループ内の処理においても主要な要素である

を満たすものだということができると思います。

極端な例

逆に言えば、上のルールを満たしさえしていれば、一般に言われる「繰り返し回数がわかっている」を満たさない場合においても、for文を使って書けるものが存在することになります。以下の例はどうでしょうか。

class Iterator {
  index = 0;
  constructor(list) {
    this.list = list;
  }
  get currentItem() {
    return this.list[this.index];
  }
  next() {
    this.index++;
  }
}

let count = 0;
for (
  const iterator = new Iterator([1, 2, 3, 4, 5]);
  iterator.currentItem;
  iterator.next()
) {
  console.log(iterator.currentItem);
  count++;
}
console.log(`個数: ${count}`);

あまり違和感はない・・・ですよね???


  1. JavaScriptでは、配列の範囲外のインデックスにアクセスがあった場合、エラーとはならずundefinedというfalsyな値を返します。このため、nextメソッドは実行されるたびに要素を1つずつ返却し、全て終わるとundefinedを返すという挙動を示します。 

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

国際ブランドの特徴

国際ブランドの特徴

前回の投稿で国際ブランドについてまとめましたが、今回はその国際ブランドごとに特徴があるのでそれをまとめていきたいと思います。

各国際ブランドの特徴

取引件数で世界一のVisa

Visaはクレジットカード取引件数で世界一を誇る国際ブランドだ。先ほど挙げたザ・ニルソン・レポートによると、Visaのシェアは44%。クレジットカード決済する人の半数近くがVisa付きのカードで支払っている計算だ。ただし、自社でクレジットカードを発行しているわけではなく、世界各国のカード会社がライセンスを受けてカードを発行している。日本でもさまざまなカード会社から発行されており、最近はプリペイドカードやデビットカードも増えている。発行カード会社を問わないVisaカード共通の優待やキャンペーンが随時実施されており、特に海外観光地のショッピング施設や免税店では、割引やギフトのプレゼントなどが頻繁に行われている。また、ゴールドカードやプラチナカードだけのサービスも提供。海外では「Visa」または「Plus」マークの付いたATMであれば、現地通貨のキャッシングもできる。

二大国際ブランドの一角をなすMastercard

Mastercardは世界2位のシェアを持ち、Visaと並んで二大国際ブランドと呼ばれる。Visa同様に自社ではクレジットカードを発行せず、ライセンスを受けたカード会社が発行している。日本ではクレジットカードに加えてプリペイドカードも発行されているが、デビットカードはない。

発行カード会社を問わないMastercard共通の優待やキャンペーンが随時あり、近年は「プライスレス・シティ」として体験型イベントでの特典が増加中。ゴールドカードやプラチナカードだけのサービスも提供されている。海外では「Mastercard」または「Cirrus」マークの付いたATMであれば、現地通貨のキャッシングも可能だ。

日本人向けのサービスが充実したJCB

JCBは唯一の日本発の国際ブランド。二大国際ブランドに比べると世界全体で見た加盟店数は劣るものの、日本国内では大差なく、海外でも日本人の多い都市では使える場所も多い。
発行カード会社を問わないJCBカード共通の優待やキャンペーンも随時実施されている。スポンサーとなっている東京ディズニーランド、東京ディズニーシー、ユニバーサル・スタジオ・ジャパン、こども向け職業体験型テーマパークのキッザニアへの抽選招待企画も毎年開催している。

海外旅行における日本人向けのサポートが充実していることも特徴で、世界約60か所に設置された海外サービス窓口の「JCBプラザ ラウンジ」と「JCBプラザ」も利用可能だ。また、海外では「JCB」または「Cirrus」マークの付いたATMであれば、現地通貨のキャッシングもできる。

高いステータス性で知られるアメックス

アメリカン・エキスプレスは「アメックス」の略称で知られ、日本国内では提携によりJCB加盟店でも利用できる(一部除く)。自社で発行するクレジットカードは、高いステータス性と充実したサービスが特徴で、スタンダードなものでも年会費は1万2000円(消費税抜き)。一方で、クレディセゾンや三菱UFJニコスなど提携カード会社からは、年会費無料のカードも発行されている。

自社でのみクレジットカードを発行し高い品質を保つダイナースクラブ

ダイナースクラブは世界初のクレジットカードといわれており、富裕層の会員が多いことで知られる。カードは自社(日本では三井住友トラストクラブが運営)でのみ発行しており、スタンダードなものでも年会費は2万2000円(消費税抜き)と、高い部類に入る。

自社発行のみのため、国際ブランドをダイナースクラブにしたい場合、日本では三井住友トラストクラブ発行のカードを選ぶことになる。そのためカード会社としてのサービスと国際ブランドとしてのサービスに区別はないが、旅行や娯楽に関する優待が充実しており、会員限定イベントなども開催されている。

中国以外でも加盟店が急増している銀聯(ぎんれん)

銀聯は中国発の国際ブランドで、英語表記では「UnionPay(ユニオンペイ)」。中国国内だけでなく、中国人観光客が多いエリアを中心に、世界各地で加盟店が増えており、日本でも約70万店で利用可能だ。日本の百貨店や家電量販店などでは、銀聯ユーザーのために割引を設けている場合もある。ただし、日本人は対象外となる店もあるので注意。

日本ではカード発行されていないディスカバー

国際ブランドとしてのディスカバーが付いたクレジットカードは日本で発行されていないものの、米国を中心に5,000万人以上のカード会員を有する。

国際ブランドまとめ

Visa(ビザ)
・加盟店数が多い
・海外観光地のショッピング施設などでの特典がある
・Apple Payのオンライン決済対応してない
Mastercard(マスターカード)
・加盟店数が多い
・体験型イベントの特典がある
・Apple Payのオンライン決済に対応
このような形でそれぞれのブランドによって特徴が違います。自分に合ったブランドを使うことでよりよい生活を送れると思います。
JCB(ジェーシービー)
・海外旅行での日本人向けサポートが充実
・東京ディズニーランドなど国内テーマパークでの特典がある(抽選)
・Apple Payのオンライン決済に対応
アメリカン・エキスプレス
・ホテルやレストランでの優待が充実
・Apple Payのオンライン決済に対応
・自社発行のカードは高いステータス性で人気
ダイナースクラブ
・クレジットカードは自社発行のみでカードのサービス=国際ブランドのサービス
・旅行・娯楽に関する優待が充実
・Apple Pay自体に非対応
銀聯(ぎんれん)
・中国人が多いエリア中心に加盟店急増中
・国内百貨店や家電量販店などでは割引がある場合も
・中国国内では銀聯のみ対応の店舗も

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

Java SDKを使ってImage Searchを使った商品の類似検索を試す【検索編】

今回の目的(Java SDKを使って検索してみる)

前回、Image Searchの設定と画像取り込みまで終わりました。今回は、前回作ったImage Searchにプログラムでアクセスしてみようと思います。

実装環境の構築

今回はJavaでプロジェクト作成します。Javaの開発環境が整っていない方は、JavaとGradleのインストールを行っておいてください。(サンプルはGradleで作成していますが、Mavenでも構いません)

参考:Windows+Scoopの場合

PS C:\Users\user> scoop install oraclejdk14
PS C:\Users\user> scoop install gradle

Javaプロジェクトの作成

それでは、Javaのプロジェクトを作ってみましょう

プロジェクトディレクトリの作成

PS C:\Users\user\Documents\temp> mkdir imagesearch-sample


    Directory: C:\Users\user\Documents\temp

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d----          2020/05/19    15:59                imagesearch-sample

PS C:\Users\user\Documents\temp> cd .\imagesearch-sample\
PS C:\Users\user\Documents\temp\imagesearch-sample>

Javaプロジェクトの作成

PS C:\Users\user\Documents\temp\imagesearch-sample> gradle init

Select type of project to generate:
  1: basic
  2: application
  3: library
  4: Gradle plugin
Enter selection (default: basic) [1..4] 2

Select implementation language:
  1: C++
  2: Groovy
  3: Java
  4: Kotlin
  5: Swift
Enter selection (default: Java) [1..5] 3

Select build script DSL:
  1: Groovy
  2: Kotlin
Enter selection (default: Groovy) [1..2] 1

Select test framework:
  1: JUnit 4
  2: TestNG
  3: Spock
  4: JUnit Jupiter
Enter selection (default: JUnit 4) [1..4] 1

Project name (default: imagesearch-sample):
Source package (default: imagesearch.sample):

> Task :init
Get more help with your project: https://docs.gradle.org/6.4.1/userguide/tutorial_java_projects.html

BUILD SUCCESSFUL in 29s
2 actionable tasks: 2 executed
PS C:\Users\user\Documents\temp\imagesearch-sample>
PS C:\Users\user\Documents\temp\imagesearch-sample> tree /F
フォルダー パスの一覧:  ボリューム Windows
ボリューム シリアル番号は 9411-0B65 です
C:.
│  .gitattributes
│  .gitignore
│  build.gradle
│  gradlew
│  gradlew.bat
│  settings.gradle
│
├─.gradle
│  ├─6.4.1
│  │  │  gc.properties
│  │  │
│  │  ├─executionHistory
│  │  │      executionHistory.bin
│  │  │      executionHistory.lock
│  │  │
│  │  ├─fileChanges
│  │  │      last-build.bin
│  │  │
│  │  ├─fileHashes
│  │  │      fileHashes.bin
│  │  │      fileHashes.lock
│  │  │
│  │  └─vcsMetadata-1
│  ├─buildOutputCleanup
│  │      buildOutputCleanup.lock
│  │      cache.properties
│  │      outputFiles.bin
│  │
│  ├─checksums
│  │      checksums.lock
│  │
│  └─vcs-1
│          gc.properties
│
├─gradle
│  └─wrapper
│          gradle-wrapper.jar
│          gradle-wrapper.properties
│
└─src
    ├─main
    │  ├─java
    │  │  └─imagesearch
    │  │      └─sample
    │  │              App.java
    │  │
    │  └─resources
    └─test
        ├─java
        │  └─imagesearch
        │      └─sample
        │              AppTest.java
        │
        └─resources
PS C:\Users\user\Documents\temp\imagesearch-sample>

依存関係の追加

Alibaba CloudのSDKを追加します。

build.gradle
/*
 * This file was generated by the Gradle 'init' task.
 *
 * This generated file contains a sample Java project to get you started.
 * For more details take a look at the Java Quickstart chapter in the Gradle
 * User Manual available at https://docs.gradle.org/6.4.1/userguide/tutorial_java_projects.html
 */

plugins {
    // Apply the java plugin to add support for Java
    id 'java'

    // Apply the application plugin to add support for building a CLI application.
    id 'application'
}

repositories {
    // Use jcenter for resolving dependencies.
    // You can declare any Maven/Ivy/file repository here.
    jcenter()
}

dependencies {
    // This dependency is used by the application.
    implementation 'com.google.guava:guava:28.2-jre'

    // Alibaba Cloud
    compile 'com.aliyun:aliyun-java-sdk-imagesearch:2.0.0'
    compile 'com.aliyun:aliyun-java-sdk-core:[4.3.2,5.0.0)'

    // Use JUnit test framework
    testImplementation 'junit:junit:4.12'
}

application {
    // Define the main class for the application.
    mainClassName = 'imagesearch.sample.App'
}

前回作成したImage Searchにアクセスする

アクセスキーとシークレットキーは前回作成したものを使用します。
また、リージョンとエンドポイントについては、以下の表から設定します。今回は日本(東京)を設定します。

リージョン エンドポイント
シンガポール imagesearch.ap-southeast-1.aliyuncs.com
中国 (香港) imagesearch.cn-hongkong.aliyuncs.com
日本 (東京) imagesearch.ap-northeast-1.aliyuncs.com
オーストラリア (シドニー) imagesearch.ap-southeast-2.aliyuncs.com

検索対象の画像は/src/resources の中に置きました。今回は "search01.jpg"という名前で作成しています。
2020-05-19.png

App.java
/*
 * This Java source file was generated by the Gradle 'init' task.
 */
package imagesearch.sample;

import java.io.InputStream;
import java.util.Base64;
import java.util.List;

import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.imagesearch.model.v20190325.SearchImageRequest;
import com.aliyuncs.imagesearch.model.v20190325.SearchImageResponse;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;

public class App {
    /** AccessKey */
    private static final String ACCESS_KEY = "XXXXXXXXXXXXXXXXXXX";
    /** SeacretKey */
    private static final String KEY_SEACRET = "YYYYYYYYYYYYYYYYYY";

    public static void main(String[] args) throws Exception {
                //初期化(Tokyoリージョン)
                DefaultProfile.addEndpoint("ap-northeast-1", "ImageSearch", "imagesearch.ap-northeast-1.aliyuncs.com");
                IClientProfile profile = DefaultProfile.getProfile("ap-northeast-1", ACCESS_KEY, KEY_SEACRET);
                IAcsClient client = new DefaultAcsClient(profile);

                //検索用のリクエストを作成
                SearchImageRequest request = new SearchImageRequest();
                // 入力必須。Image Search インスタンス名。
                request.setInstanceName("itemsearch");
                // 画像をBase64に変換
                ClassLoader loader = Thread.currentThread().getContextClassLoader();
                InputStream is = loader.getResourceAsStream("search01.jpg");
                String image = Base64.getEncoder().encodeToString(is.readAllBytes());
                //画像で検索
                request.setPicContent(image);
                SearchImageResponse response = client.getAcsResponse(request);

                // 検索結果
                Boolean checkShowJsonItemName = response.checkShowJsonItemName();
                Integer code = response.getCode();
                SearchImageResponse.Head head = response.getHead();
                Integer docsFound = head.getDocsFound();
                Integer docsReturn = head.getDocsReturn();
                Integer searchTime = head.getSearchTime();
                String msg = response.getMsg();
                String requestId = response.getRequestId();
                Boolean success = response.getSuccess();
                SearchImageResponse.PicInfo picInfo = response.getPicInfo();
                List<SearchImageResponse.PicInfo.Category> categories = picInfo.getAllCategories();
                Integer categoryId = picInfo.getCategoryId();
                String region = picInfo.getRegion();

                System.out.printf("checkShowJsonItemName: %s%ncode: %s%ndocsFound: %s%nsearchTime:%s %nmsg: %s%nrequestId: %s%nsucess: %s%ncategoryId: %s%nregion: %s%n", checkShowJsonItemName, code, docsFound, docsReturn, searchTime, msg, requestId, success, categoryId, region);
                categories.forEach(s -> {
                    Integer id = s.getId();
                    String name = s.getName();
                    System.out.printf("Categories %n\tid: %s, name: %s%n", id, name);
                });
                List<SearchImageResponse.Auction> list = response.getAuctions();
                list.forEach(s -> {
                    System.out.println("-----------");
                    String productid = s.getProductId();
                    Integer categoryid = s.getCategoryId();
                    String customCountent = s.getCustomContent();
                    Integer intAttr = s.getIntAttr();
                    String picName = s.getPicName();
                    String sortExpValues = s.getSortExprValues();
                    String strAttr = s.getStrAttr();
                    System.out.printf("productId: %s %nCategoryId: %s %ncustomContent: %s %nintAttr: %s %npicName: %s %nsortExpValues: %s %nStrAttr: %s%n", productid, categoryid, customCountent, intAttr, picName, sortExpValues, strAttr);
                });
    }
}

checkShowJsonItemName: false
code: 0
docsFound: 11
searchTime:10
msg: 99
requestId: success
sucess: 85B66F6E-9932-4BF6-A05F-4AA0AF4D7CD5
categoryId: true
region: 9
Categories
        id: 0, name: Tops
Categories
        id: 1, name: Dress
Categories 
        id: 2, name: Bottoms
Categories
        id: 3, name: Bag
Categories
        id: 4, name: Shoes
Categories 
        id: 5, name: Accessories
Categories
        id: 6, name: Snack
Categories
        id: 7, name: Makeup
Categories
        id: 8, name: Bottle
Categories
        id: 9, name: Furniture
Categories
        id: 20, name: Toy
Categories
        id: 21, name: Underwear
Categories
        id: 22, name: Digital device
Categories
        id: 88888888, name: Other
-----------
productId: 1011
CategoryId: 9
customContent: k1:v12,k2:v211,k3:v311
intAttr: null 
picName: 12.jpg
sortExpValues: 3.07306838035583;217
StrAttr: null
-----------
productId: 1010
CategoryId: 9
customContent: k1:v11,k2:v210,k3:v310
intAttr: null
picName: 11.jpg
sortExpValues: 2.97270393371582;222
StrAttr: null
-----------
productId: 1008
CategoryId: 9
customContent: k1:v09,k2:v208,k3:v308
intAttr: null
picName: 09.jpg
sortExpValues: 2.87724995613098;238
StrAttr: null
-----------
productId: 1009
CategoryId: 9
customContent: k1:v10,k2:v209,k3:v309
intAttr: null
picName: 10.jpg 
sortExpValues: 2.79507827758789;235
StrAttr: null
-----------
productId: 1001
CategoryId: 9
customContent: k1:v02,k2:v201,k3:v301
intAttr: null
picName: 02.jpg
sortExpValues: 2.67687916755676;251
StrAttr: null
-----------
productId: 1004
CategoryId: 9
customContent: k1:v05,k2:v204,k3:v304 
intAttr: null
picName: 05.jpg
sortExpValues: 2.67470407485962;249
StrAttr: null
-----------
productId: 1005
CategoryId: 9
customContent: k1:v06,k2:v205,k3:v305
intAttr: null
picName: 06.jpg
sortExpValues: 2.66586232185364;254
StrAttr: null
-----------
productId: 1003
CategoryId: 9
customContent: k1:v04,k2:v203,k3:v303
intAttr: null
picName: 04.jpg
sortExpValues: 2.63756942749023;255
StrAttr: null
-----------
productId: 1000
CategoryId: 9
customContent: k1:v01,k2:v200,k3:v300 
intAttr: null 
picName: 01.jpg
sortExpValues: 2.57631182670593;270
StrAttr: null
-----------
productId: 1006
CategoryId: 9
customContent: k1:v07,k2:v206,k3:v306
intAttr: null
picName: 07.jpg
sortExpValues: 2.52564144134521;253
StrAttr: null

前回、管理画面から投げた結果と同じ結果が出力されました。

ソースコード解説

App.java
//初期化(Tokyoリージョンのケース
DefaultProfile.addEndpoint({regionId}, {product}, {endpoint});
IClientProfile profile = DefaultProfile.getProfile({regionId}, {accessKeyId}, {secret})
IAcsClient client = new DefaultAcsClient(profile);

regionIdには、東京リージョンの場合「ap-northeast-1」を指定します。productには「ImageSearch」、endpointには、上記リストのエンドポイントURLを設定します。
productは固定の値ですね。regionIdは他のリージョンの場合の値を調べるのが面倒ですね。。。

App.java
//検索用のリクエストを作成
SearchImageRequest request = new SearchImageRequest();
// 入力必須。Image Search インスタンス名。
request.setInstanceName("itemsearch");

検索には検索用のリクエスト、追加、削除にはそれぞれ追加用、削除用のリクエストオブジェクトが用意されています。
インスタンス名は注意が必要です。

01.png

「ID」ではなく、「名前」の方を指定する必要があります。

App.java
//画像で検索
request.setPicContent({BASE64エンコードした画像});
SearchImageResponse response = client.getAcsResponse(request);

今回は画像で検索しました。他にも登録した画像で検索する方法や、パラメータによる絞り込みが可能です。これらについては、次回検証します。

今回はここまで。SDKを使うと簡単に検索できますね。

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

Java SDKを使ってImage Searchの類似検索を試す【検索編】

今回の目的(Java SDKを使って検索してみる)

前回、Image Searchの設定と画像取り込みまで終わりました。今回は、前回作ったImage Searchにプログラムでアクセスしてみようと思います。

実装環境の構築

今回はJavaでプロジェクト作成します。Javaの開発環境が整っていない方は、JavaとGradleのインストールを行っておいてください。(サンプルはGradleで作成していますが、Mavenでも構いません)

参考:Windows+Scoopの場合

PS C:\Users\user> scoop install oraclejdk14
PS C:\Users\user> scoop install gradle

Javaプロジェクトの作成

それでは、Javaのプロジェクトを作ってみましょう

プロジェクトディレクトリの作成

PS C:\Users\user\Documents\temp> mkdir imagesearch-sample


    Directory: C:\Users\user\Documents\temp

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d----          2020/05/19    15:59                imagesearch-sample

PS C:\Users\user\Documents\temp> cd .\imagesearch-sample\
PS C:\Users\user\Documents\temp\imagesearch-sample>

Javaプロジェクトの作成

PS C:\Users\user\Documents\temp\imagesearch-sample> gradle init

Select type of project to generate:
  1: basic
  2: application
  3: library
  4: Gradle plugin
Enter selection (default: basic) [1..4] 2

Select implementation language:
  1: C++
  2: Groovy
  3: Java
  4: Kotlin
  5: Swift
Enter selection (default: Java) [1..5] 3

Select build script DSL:
  1: Groovy
  2: Kotlin
Enter selection (default: Groovy) [1..2] 1

Select test framework:
  1: JUnit 4
  2: TestNG
  3: Spock
  4: JUnit Jupiter
Enter selection (default: JUnit 4) [1..4] 1

Project name (default: imagesearch-sample):
Source package (default: imagesearch.sample):

> Task :init
Get more help with your project: https://docs.gradle.org/6.4.1/userguide/tutorial_java_projects.html

BUILD SUCCESSFUL in 29s
2 actionable tasks: 2 executed
PS C:\Users\user\Documents\temp\imagesearch-sample>
PS C:\Users\user\Documents\temp\imagesearch-sample> tree /F
フォルダー パスの一覧:  ボリューム Windows
ボリューム シリアル番号は 9411-0B65 です
C:.
│  .gitattributes
│  .gitignore
│  build.gradle
│  gradlew
│  gradlew.bat
│  settings.gradle
│
├─.gradle
│  ├─6.4.1
│  │  │  gc.properties
│  │  │
│  │  ├─executionHistory
│  │  │      executionHistory.bin
│  │  │      executionHistory.lock
│  │  │
│  │  ├─fileChanges
│  │  │      last-build.bin
│  │  │
│  │  ├─fileHashes
│  │  │      fileHashes.bin
│  │  │      fileHashes.lock
│  │  │
│  │  └─vcsMetadata-1
│  ├─buildOutputCleanup
│  │      buildOutputCleanup.lock
│  │      cache.properties
│  │      outputFiles.bin
│  │
│  ├─checksums
│  │      checksums.lock
│  │
│  └─vcs-1
│          gc.properties
│
├─gradle
│  └─wrapper
│          gradle-wrapper.jar
│          gradle-wrapper.properties
│
└─src
    ├─main
    │  ├─java
    │  │  └─imagesearch
    │  │      └─sample
    │  │              App.java
    │  │
    │  └─resources
    └─test
        ├─java
        │  └─imagesearch
        │      └─sample
        │              AppTest.java
        │
        └─resources
PS C:\Users\user\Documents\temp\imagesearch-sample>

依存関係の追加

Alibaba CloudのSDKを追加します。

build.gradle
/*
 * This file was generated by the Gradle 'init' task.
 *
 * This generated file contains a sample Java project to get you started.
 * For more details take a look at the Java Quickstart chapter in the Gradle
 * User Manual available at https://docs.gradle.org/6.4.1/userguide/tutorial_java_projects.html
 */

plugins {
    // Apply the java plugin to add support for Java
    id 'java'

    // Apply the application plugin to add support for building a CLI application.
    id 'application'
}

repositories {
    // Use jcenter for resolving dependencies.
    // You can declare any Maven/Ivy/file repository here.
    jcenter()
}

dependencies {
    // This dependency is used by the application.
    implementation 'com.google.guava:guava:28.2-jre'

    // Alibaba Cloud
    compile 'com.aliyun:aliyun-java-sdk-imagesearch:2.0.0'
    compile 'com.aliyun:aliyun-java-sdk-core:[4.3.2,5.0.0)'

    // Use JUnit test framework
    testImplementation 'junit:junit:4.12'
}

application {
    // Define the main class for the application.
    mainClassName = 'imagesearch.sample.App'
}

前回作成したImage Searchにアクセスする

アクセスキーとシークレットキーは前回作成したものを使用します。
また、リージョンとエンドポイントについては、以下の表から設定します。今回は日本(東京)を設定します。

リージョン エンドポイント
シンガポール imagesearch.ap-southeast-1.aliyuncs.com
中国 (香港) imagesearch.cn-hongkong.aliyuncs.com
日本 (東京) imagesearch.ap-northeast-1.aliyuncs.com
オーストラリア (シドニー) imagesearch.ap-southeast-2.aliyuncs.com

検索対象の画像は/src/resources の中に置きました。今回は "search01.jpg"という名前で作成しています。
2020-05-19.png

App.java
/*
 * This Java source file was generated by the Gradle 'init' task.
 */
package imagesearch.sample;

import java.io.InputStream;
import java.util.Base64;
import java.util.List;

import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.imagesearch.model.v20190325.SearchImageRequest;
import com.aliyuncs.imagesearch.model.v20190325.SearchImageResponse;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;

public class App {
    /** AccessKey */
    private static final String ACCESS_KEY = "XXXXXXXXXXXXXXXXXXX";
    /** SeacretKey */
    private static final String KEY_SEACRET = "YYYYYYYYYYYYYYYYYY";

    public static void main(String[] args) throws Exception {
                //初期化(Tokyoリージョン)
                DefaultProfile.addEndpoint("ap-northeast-1", "ImageSearch", "imagesearch.ap-northeast-1.aliyuncs.com");
                IClientProfile profile = DefaultProfile.getProfile("ap-northeast-1", ACCESS_KEY, KEY_SEACRET);
                IAcsClient client = new DefaultAcsClient(profile);

                //検索用のリクエストを作成
                SearchImageRequest request = new SearchImageRequest();
                // 入力必須。Image Search インスタンス名。
                request.setInstanceName("itemsearch");
                // 画像をBase64に変換
                ClassLoader loader = Thread.currentThread().getContextClassLoader();
                InputStream is = loader.getResourceAsStream("search01.jpg");
                String image = Base64.getEncoder().encodeToString(is.readAllBytes());
                //画像で検索
                request.setPicContent(image);
                SearchImageResponse response = client.getAcsResponse(request);

                // 検索結果
                Boolean checkShowJsonItemName = response.checkShowJsonItemName();
                Integer code = response.getCode();
                SearchImageResponse.Head head = response.getHead();
                Integer docsFound = head.getDocsFound();
                Integer docsReturn = head.getDocsReturn();
                Integer searchTime = head.getSearchTime();
                String msg = response.getMsg();
                String requestId = response.getRequestId();
                Boolean success = response.getSuccess();
                SearchImageResponse.PicInfo picInfo = response.getPicInfo();
                List<SearchImageResponse.PicInfo.Category> categories = picInfo.getAllCategories();
                Integer categoryId = picInfo.getCategoryId();
                String region = picInfo.getRegion();

                System.out.printf("checkShowJsonItemName: %s%ncode: %s%ndocsFound: %s%nsearchTime:%s %nmsg: %s%nrequestId: %s%nsucess: %s%ncategoryId: %s%nregion: %s%n", checkShowJsonItemName, code, docsFound, docsReturn, searchTime, msg, requestId, success, categoryId, region);
                categories.forEach(s -> {
                    Integer id = s.getId();
                    String name = s.getName();
                    System.out.printf("Categories %n\tid: %s, name: %s%n", id, name);
                });
                List<SearchImageResponse.Auction> list = response.getAuctions();
                list.forEach(s -> {
                    System.out.println("-----------");
                    String productid = s.getProductId();
                    Integer categoryid = s.getCategoryId();
                    String customCountent = s.getCustomContent();
                    Integer intAttr = s.getIntAttr();
                    String picName = s.getPicName();
                    String sortExpValues = s.getSortExprValues();
                    String strAttr = s.getStrAttr();
                    System.out.printf("productId: %s %nCategoryId: %s %ncustomContent: %s %nintAttr: %s %npicName: %s %nsortExpValues: %s %nStrAttr: %s%n", productid, categoryid, customCountent, intAttr, picName, sortExpValues, strAttr);
                });
    }
}

checkShowJsonItemName: false
code: 0
docsFound: 11
searchTime:10
msg: 99
requestId: success
sucess: 85B66F6E-9932-4BF6-A05F-4AA0AF4D7CD5
categoryId: true
region: 9
Categories
        id: 0, name: Tops
Categories
        id: 1, name: Dress
Categories 
        id: 2, name: Bottoms
Categories
        id: 3, name: Bag
Categories
        id: 4, name: Shoes
Categories 
        id: 5, name: Accessories
Categories
        id: 6, name: Snack
Categories
        id: 7, name: Makeup
Categories
        id: 8, name: Bottle
Categories
        id: 9, name: Furniture
Categories
        id: 20, name: Toy
Categories
        id: 21, name: Underwear
Categories
        id: 22, name: Digital device
Categories
        id: 88888888, name: Other
-----------
productId: 1011
CategoryId: 9
customContent: k1:v12,k2:v211,k3:v311
intAttr: null 
picName: 12.jpg
sortExpValues: 3.07306838035583;217
StrAttr: null
-----------
productId: 1010
CategoryId: 9
customContent: k1:v11,k2:v210,k3:v310
intAttr: null
picName: 11.jpg
sortExpValues: 2.97270393371582;222
StrAttr: null
-----------
productId: 1008
CategoryId: 9
customContent: k1:v09,k2:v208,k3:v308
intAttr: null
picName: 09.jpg
sortExpValues: 2.87724995613098;238
StrAttr: null
-----------
productId: 1009
CategoryId: 9
customContent: k1:v10,k2:v209,k3:v309
intAttr: null
picName: 10.jpg 
sortExpValues: 2.79507827758789;235
StrAttr: null
-----------
productId: 1001
CategoryId: 9
customContent: k1:v02,k2:v201,k3:v301
intAttr: null
picName: 02.jpg
sortExpValues: 2.67687916755676;251
StrAttr: null
-----------
productId: 1004
CategoryId: 9
customContent: k1:v05,k2:v204,k3:v304 
intAttr: null
picName: 05.jpg
sortExpValues: 2.67470407485962;249
StrAttr: null
-----------
productId: 1005
CategoryId: 9
customContent: k1:v06,k2:v205,k3:v305
intAttr: null
picName: 06.jpg
sortExpValues: 2.66586232185364;254
StrAttr: null
-----------
productId: 1003
CategoryId: 9
customContent: k1:v04,k2:v203,k3:v303
intAttr: null
picName: 04.jpg
sortExpValues: 2.63756942749023;255
StrAttr: null
-----------
productId: 1000
CategoryId: 9
customContent: k1:v01,k2:v200,k3:v300 
intAttr: null 
picName: 01.jpg
sortExpValues: 2.57631182670593;270
StrAttr: null
-----------
productId: 1006
CategoryId: 9
customContent: k1:v07,k2:v206,k3:v306
intAttr: null
picName: 07.jpg
sortExpValues: 2.52564144134521;253
StrAttr: null

前回、管理画面から投げた結果と同じ結果が出力されました。

ソースコード解説

App.java
//初期化(Tokyoリージョンのケース
DefaultProfile.addEndpoint({regionId}, {product}, {endpoint});
IClientProfile profile = DefaultProfile.getProfile({regionId}, {accessKeyId}, {secret})
IAcsClient client = new DefaultAcsClient(profile);

regionIdには、東京リージョンの場合「ap-northeast-1」を指定します。productには「ImageSearch」、endpointには、上記リストのエンドポイントURLを設定します。
productは固定の値ですね。regionIdは他のリージョンの場合の値を調べるのが面倒ですね。。。

App.java
//検索用のリクエストを作成
SearchImageRequest request = new SearchImageRequest();
// 入力必須。Image Search インスタンス名。
request.setInstanceName("itemsearch");

検索には検索用のリクエスト、追加、削除にはそれぞれ追加用、削除用のリクエストオブジェクトが用意されています。
インスタンス名は注意が必要です。

01.png

「ID」ではなく、「名前」の方を指定する必要があります。

App.java
//画像で検索
request.setPicContent({BASE64エンコードした画像});
SearchImageResponse response = client.getAcsResponse(request);

今回は画像で検索しました。他にも登録した画像で検索する方法や、パラメータによる絞り込みが可能です。これらについては、次回検証します。

今回はここまで。SDKを使うと簡単に検索できますね。

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

国際ブランドについて

国際ブランドについて

今回は、3つ目のクレジットにおいて最重要ポジションの国際ブランドについてまとめていきたいと思います。

国際ブランドとは

クレジットカードの国際ブランドとはVisaやMastercardなど決済ネットワークのブランドのことで、どこで使えるかを示すもの。Visaのマークが付いたカードであればVisaでの決済を取り扱っている加盟店、MastercardであればMastercard加盟店で支払いができ、「国際」とあるとおり、加盟店であれば世界中どこでも支払いができる。

VisaやMastercard=クレジットカードではない

VisaやMastercard=クレジットカードという認識の人も少なくないが、これは間違い。クレジットは「信用」を意味する言葉で、後払いのこと。つまり、後払いできるカードであれば、国際ブランドがなくてもクレジットカードであり、現在も百貨店やガソリンスタンドなどでは、ハウスカードと呼ばれる特定の店でしか使えないクレジットカードも発行されている。

国際ブランドがついたプリペイドカードやデビットカードも増えている

また、最近では事前にお金をチャージして利用するプリペイドカード、銀行口座から即時引き落としで支払うデビットカードでも、国際ブランド付きのものが増えている。これらは支払い方法が異なるだけで、基本的にクレジットカード同様に各加盟店で利用できる。ただし、システムの都合上で非対応の加盟店も一部ある。

国際ブランドは7つ。うちVisa、Mastercardが世界シェア7割強

現在、国際ブランドとされているのは「Visa」「Mastercard」「JCB(ジェーシービー)」「アメリカン・エキスプレス」「ダイナースクラブ」「銀聯(ぎんれん)」「ディスカバー」の7つ。うち、VisaとMastercardは世界的に加盟店数が多く、その分クレジットカード保有者も多い。

カード・モバイル決済業界の専門誌「ザ・ニルソン・レポート」によると、2015年の世界のクレジットカード取引件数で、VisaとMastercardが占める割合は7割強。10人いたら7人以上がVisaかMastercard付きのクレジットカードで決済している。
F12EB890-B61A-42EA-9015-AE11C938F107.png

そのほか、シェアは小さいものの、JCB、アメリカン・エキスプレス、ダイナースクラブは「T&Eカード」と呼ばれ、旅行(Travel)や娯楽(Entertainment)に関するサービスが充実しているという特徴がある。銀聯は中国、ディスカバーは米国を中心に普及しているブランドだ。

国際ブランドまとめ

今回は、国際ブランドについてまとめてみました。アクワイアラやイシュアに比べててボリュームが多かったです。
国際ブランドがアクワイアラに許可を出して加盟店を増やしている。

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

イシュアについて

イシュアについて

前回アクワイアラについて投稿させて頂きましたが、今回は、それと密接に関係のあるイシュアについてまとめていきたいと思います。

イシュアとは何か

イシュアとは、消費者に対しクレジットカードを発行する会社を指します。

カードの券面に記載されている

Visa®
マスターカード®
JCB®
American Express®
Diners Club®
などは「国際ブランド」で、国際的な決済機能を提供する会社です。

イシュア(カード発行会社)は、この国際ブランドからライセンスを取得し、消費者に国際ブランド付きのクレジットカードを発行します。

つまり、国際ブランドとクレジットカード契約者の間を取り次ぐのがイシュアの役割です。

イシュアの役割

イシュアには、大きく分けて3つの役割があります。

1.クレジットカードの発行

1つ目はクレジットカードの発行です。

前項でもご紹介しましたが、イシュアは国際ブランドとクレジットカード契約者の間を取り次ぐ役割があります。

より多くの会員を増やすため、イシュアによってはポイントの付与や割引キャンペーンを実施している場合があります。

2. カード会員の管理・利用代金の請求

2つ目はカード会員の管理・利用代金の請求です。

イシュアは会員のクレジットカードの引き落とし情報や利用状況を管理し、利用明細を発行、請求(引き落とし)をする役割があります。

3.カードの利用状況の監視

3つ目はカードの利用状況の監視です。

クレジットカードを利用した不正利用が行われないよう、状況を監視するのもイシュアの役割です。

余談ですが、カード券面に記載されているセキュリティコードもイシュアが発行します。

また、インターネット上で利用明細を確認する際の本人認証もイシュアが行います。

イシュアとアクワイアラの違い

日本では同じ会社が業務として行っている場合が多く、混同されがちなイシュアとアクワイアラ。ですが、実際には役割に大きな違いがあります。
イシュアはクレジットカードを発行するなどの役割があるのに対して、アクワイアラはクレジットの加盟店を獲得し管理する役割を担っています。

イシュアまとめ

今回は、イシュアについてまとめてみました。
イシュアは4つのポジションの中で我々消費者と1番密接で関わりが多いポジションだとわかりました。
簡単な違いとしてはイシュアは消費者、アクワイワラは企業を相手にしていると言うことです。

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

アクワイアラについて

アクワイアラについて

前回クレジットカードの仕組みについて投稿させて頂きました。4つのポジションがあり今回は、アクワイアラについて詳しく見ていこうと思います。

アクワイアラ3つの役割

1.加盟店を増やす

アクワイアラは、クレジットカードが使用できる加盟店を集め、増やしていく役割を担っています。国際ブランドを利用するためには、事業者さまは加盟店契約を結ばないと利用できません。そのため、クレジットカードを導入したいと検討している事業者さまは、アクワイアラと契約を結ぶことになります。

2.加盟店を審査する

アクワイアラには、加盟店を慎重に審査する役割があります。取り扱っている商品やサービスがクレジットカードでの販売に向いているかなどを審査します。契約締結後に、加盟店の管理も行います。

3.支払いを取り次ぐ

イシュアは、お客さま(カード会員)から支払い料金を回収しますが、加盟店と契約を結んでいないため、そのままではお支払いができません。そこで、加盟店と契約を結んでいるアクワイアラが支払い料金を取り次ぎ、アクワイアラが責任を持って加盟店にお支払いします。

アクワイアラのまとめ

アクワイアラは加盟店を増やすことがまず第一の仕事なので大規模商業施設や店舗に声をかけ加盟店を増やしていきます。私達消費者が使ったクレジットカードのお金はアクワイアラが加盟店に払いそれをのちにイシュアが回収すると言った形になっており、我々のお金を信用で買い建て替えてくれていると言うことになります。

引用元
title

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

クレジットカードの仕組み

現在クレジット関係の業務に携わっており、初めて知ることが多くありましたのでまとめていきたいと思います。

まずはクレジットカードの仕組みについてです!

クレジットカード業界4つのポジション

アクワイワラー

加盟店の開拓、審査、管理をする会社(国際ブランドと加盟店を取り次ぐ)

イシュア

クレジットカードの発行会社(国際ブランドとお客さまを取り次ぐ)

国際ブランド

世界中で利用できる決済システムを提供するクレジットカード会社(Visa、MasterCard、JCB、American Express、Diners Club International、銀聯、Discoverなど)

決済代行業者

加盟店の審査や契約手続き、売上入金管理などを代行する会社E5C312D2-BE78-4789-9CEF-1CB368C7301C.png
この図のようにこの4つの機関が関わり合うことでクレジットカードは運営され私達の手元に届いています。
引用元
title

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

【Javaの基礎知識】変数のスコープ

はじめに

こちらの記事は、
- Javaをこれから学習し始める
- 基本的なことをおさらいしたい

という方向けです。

スコープについて

スコープとは、変数が扱える範囲のことを指します。
「ココからココまではこの変数が使えるけど、ココからココまではこの変数が使えないよ」
こんな感じです。

変数を宣言したら、{}の間がその変数を使える範囲になってきます。

このスコープを意識しないとコンパイルエラーになったりするので、
これから説明する変数でスコープの説明をしていきます。

ローカル変数

{から}までをブロックというんですが、そのブロック内で扱える変数のことをローカル変数と呼びます。
宣言した位置より後で参照することができます。

public class local {
  public static void main(String args[]){
    int a = 0;

    if(true){
      int b = 0;
    }
    a = 1;// aはmainの{}の間で参照できる
    b = 1;// bはifの外なのでコンパイルエラー
  }
   a = 2;// mainの外なのでコンパイルエラー

   public static void sub(){
     a = 3;// mainの外にあるメソッドなのでコンパイルエラー
   }
}

ローカル変数があれば、グローバル変数もあると思うかもしれませんが、
Javaにはグローバル変数というものはありません(※Javaではグローバル変数は原則使わない)。

インスタンス変数

newされる度にメモリ領域にインスタンスが作成されます。
オブジェクト.インスタンス変数名でアクセスできます。

food.java
class Food{
  String menu;//インスタンス変数
  int price;//インスタンス変数

  public void Info(){
    System.out.println("こちらは"+ menu + "です");
    System.out.println("値段は"+ price + "円です");
  }

  public void say(){
    System.out.println("揚げたて"+ menu + "は最高");
  }
}
Main.java
public class Main {
  public static void main(String args[]){
    Food potato = new Food();//メモリ上に新しい番地が確保される(Foodクラスのインスタンス化)

    potato.menu = "ポテイト";//potato(オブジェクト).menu(インスタンス変数)に"ポテイト"を代入
    potato.price = 270;//potato(オブジェクト).price(インスタンス変数)に"270"を代入

    System.out.println(potato.menu);// ポテイト

    potato.Info();
    // こちらはポテイトです
    // 値段は270円です

    potato.say();// 揚げたてポテイトは最高

    Food nugget = new Food();//メモリ上に新しい番地が確保される

    nugget.menu = "ナゲット";//nugget(オブジェクト).menu(インスタンス変数)に"ナゲット"を代入
    nugget.price = 580;//nugget(オブジェクト).price(インスタンス変数)に"580"を代入

    System.out.println(nugget.menu);// ナゲット

    nugget.Info();
    // こちらはナゲットです
    // 値段は580円です

  }

}

では、次にコンパイルエラーとなってしまう例はこちら

food.java
  public static void say(){
    System.out.println("揚げたて"+ menu + "は最高");
  }

food.javaのsayメソッドをstaticメソッドにすると、
Cannot make a static reference to the non-static field menu
てな感じで怒られるし、

Main.java
 potato.say();

この部分も、

The static method say() from the type Food should be accessed in a static way
てな感じで怒られます。

後ほど説明しますが、
staticメソッドにするなら、static変数で定義しないといけません。

thisについて

自分自身のインスタンスを指す時、thisというキーワードを用いることができます。
this.変数名でアクセスできます。なお、this.は省略可能です。

クラスのフィールドで定義されている変数と、ローカル変数の名前が同じ場合に、対象を特定するために利用されることが多いです。
クラス変数より、ローカル変数の方が優先されます。

一旦こちらの例をみてください。

public class Soccer {

  //インスタンス変数
  String player = "メッシ";

  public static void main(String[] args){

    //ローカル変数
    String player = "イニエスタ";

    System.out.println(player + "です");// イニエスタです
  }
}

先ほど述べたように、ローカル変数が優先されました。
クラス変数の方のplayerにアクセスする場合、this.変数名としてあげることで、指定できます。

thisを使ったダメな例:Soccer.java
public class Soccer {

  //インスタンス変数
  String player = "メッシ";

  public static void main(String[] args){

    //ローカル変数
    String player = "イニエスタ";

    System.out.println("ローカル変数" + player + "です");// ローカル変数イニエスタです
    System.out.println("インスタンス変数" + this.player + "です");// Cannot use this in a static context
  }
}

ダメな例にあるように、そのままthisと書けばいいというわけではありません。

正しい例:Soccer.java
class Soccer {
  String player = "メッシ";

  public void say(String player) {// 引数で区別できるようにする
      System.out.println("ローカル変数" + player + "です");// ローカル変数イニエスタです
      System.out.println("インスタンス変数" + this.player + "です");// クラス変数メッシです
  }
}

正しい例:Print.java
public class Print {
  public static void main(String[] args) {
      String player = "イニエスタ";
      Soccer sc = new Soccer();
      sc.say(player);
  }
}

static変数(クラス変数)

static修飾子は、クラス領域に定義します。
クラス名.static変数名でアクセスできます。
static変数は、オブジェクト毎に値をもちません。

例をみてみましょう。

Meiji.java
public class Meiji {
  public static String snack;// static変数化

  public void saySnack(){
    System.out.println(snack);
  }
}
Meiji2.java
public class Meiji2 {
  public static void main(String[] args){
    Meiji.snack = "きのこの山";// snackに"きのこの山"が格納される
    Meiji say1 = new Meiji();// Meijiクラスのインスタンス化
    say1.saySnack();// きのこの山
    Meiji say2 = new Meiji();
    say2.saySnack();// きのこの山

    Meiji.snack = "たけのこの里";// snackに"たけのこの里"が格納される
    say1.saySnack();// たけのこの里
    say2.saySnack();// たけのこの里
  }
}

このように、オブジェクトごとに値を持たないので、
say1とsay2が同じメモリ上の値を参照しています。

static変数の使い道としては、
まだまだ勉強中ですが、クラスからいくつインスタンスができたかを数える方法で活用できます。
先ほどのMeiji.javaとMeiji2.javaを少し書き換えてみました。

Meiji.java
public class Meiji {
  public String snack;// インスタンス変数
  public static int count = 0;// static変数を初期化する

  public void saySnack(){
    System.out.println(snack);
    Meiji.count++;// クラス名.count(static変数)で指定して、1ずつ増やす
  }

  public static void snackCnt(){ // staticメソッド(クラスメソッド)
    System.out.println(Meiji.count);
  }
}
Meiji2.java
public class Meiji2 {
  public static void main(String[] args){
    Meiji.snackCnt();// 0

    Meiji kinoko = new Meiji();// Meijiクラスのインスタンス化
    kinoko.snack = "きのこの山";//kinoko(オブジェクト).snack(インスタンス変数)に"きのこの山"を代入
    kinoko.saySnack();// きのこの山

    Meiji.snackCnt();// 1

    Meiji takenoko = new Meiji();
    takenoko.snack = "たけのこの里";
    takenoko.saySnack();// たけのこの里

    Meiji.snackCnt();// 2

  }
}

saySnackメソッド内でカウントアップしているので、
そのメソッドが呼ばれる度に数字が増えているのがわかります。

最後に

今回は変数とスコープについて学習をしました。
そして、以下の変数について特徴を記載しました。
- ローカル変数
- インスタンス変数
- static変数(クラス変数)

thisの使い方などまだ方法はいくつかあると思いますが、
ここでは割愛させていただきます。

ありがとうございました☕️

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

Javaの引数

引数

引数とはメソッドに与える追加情報のようなものです。
メソッドを呼び出すときに、一緒に引数を渡すと、メソッドの中でその値を利用できます。
メソッドに引数を渡すには、まず引数を受け取れるメソッドを定義します。
そのためにはメソッドの定義部分で、引数を受け取る変数(仮引数)を指定します。

Main.java
public static void メソッド名(データ型 変数名) {  //データ型 変数名は仮引数です
  実行する処理;
}

【例】

Main.java
class Main {
  public static void main(String[] args) {
    nameData("佐藤");
    nameData("鈴木");

  }
  public static void nameData(String name) {    // 引数を受け取るようにしてます
    System.out.println("名前は"+name+"です");
  }
}

上記の例だとnameDataはメソッドで、(String name)はデータ型 変数名です。
System.out.println("名前は"+name+"です");は実行する処理です。
「名前は佐藤です」と「名前な鈴木です」が結果として出てきます。

複数の引数

メソッドが複数の引数を受け取るためには、仮引数をコンマ(,)で区切って定義します。

Main.java
public static void メソッド名(データ型 変数名,データ型 変数名) {  //左から第一引数、第二引数です
  実行する処理;
}

【例】

Main.java
class Main {
  public static void main(String[] args) {
    nameData("佐藤",20);
    nameData("鈴木",30);

  }
  public static void nameData(String name,int age) {    // 引数を受け取るようにしてます
    System.out.println("名前は"+name+"で年齢は"+age+"です");
  }
}

「名前は佐藤で年齢は20です」と「名前な鈴木で年齢は30です」が結果として出てきます。

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

Javaのメソッド

メソッド

JavaのメソッドはJavaのファイルを実行すると、自動的にmainメソッドが実行されます。
mainメソッドが各メソッドに指示を出し、各メソッドが個々の処理を実行します。
Main.javaを実行=>mainメソッド=>各メソッド(メソッド1)(メソッド2)(メソッド3)といった具合です。
メソッドの定義

Main.java
public static void メソッド名() {
  実行する処理;
}

【例】

Main.java
class Main {  //Mainクラスのブロック
  public static void main(String[] args) {  //mainメソッドが呼ばれます
    hello();  //mainメソッドの中でhelloメソッドが呼ばれます
  }
  public static void hello() {  //helloメソッドの中でおはようが実行されます
    System.out.println("おはよう");
  }
}  //Mainクラスのブロック
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Java】BeanValidationに存在するアノテーションの実装クラスはどこにあるのか

背景

JavaやSpringでバリデーションを実装する際には@NotNull@NotBlankといったBeanValidationを用いることがあると思います。

実装をしている際に、これらのアノテーションに紐づいている実装クラスはどのようになっているのだろうかと思い、ソースコードを見てみたのですが、@ConstraintのvalidatedByが空白でした。

javax.validation.constraintsパッケージのNotBlankアノテーション1を例に出しておきます。

NotBlank.java
@Documented
@Constraint(validatedBy = { }) // ←でバリデーションの中身が書かれたクラスが指定されていない
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Repeatable(List.class)
public @interface NotBlank {

    String message() default "{javax.validation.constraints.NotBlank.message}";

    Class<?>[] groups() default { };

    Class<? extends Payload>[] payload() default { };

    /**
     * Defines several {@code @NotBlank} constraints on the same element.
     *
     * @see NotBlank
     */
    @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
    @Retention(RUNTIME)
    @Documented
    public @interface List {
        NotBlank[] value();
    }
}

今回はこのアノテーションの実装結果の場所と紐付けをしているクラスを調べました。

調査結果

Googleで検索してみると、こちらのstackoverflowで同様の質問がされており、実装クラスを知ることができました。

それがこちらのorg.hibernate.validator.internal.constraintvalidators.bv配下のNotBlankValidatorクラスです。

このことからアノテーションとその実装クラスは同じライブラリではなく、別のライブラリに存在しているということがわかりました。

また、これらを紐付けているクラスは実装クラスと同じライブラリに存在し、こちらのorg.hibernate.validator.internal.metadata.core配下のConstraintHelperクラスで紐づけられていることがわかりました。

まとめ

今回、BeanValidationのjavax.validation.constraintsに存在するアノテーションの実装クラスはorg.hibernate.validator.internal.constraintvalidators.bv配下に存在しているということがわかりました。

また、これらのクラスはorg.hibernate.validator.internal.metadata.core配下のConstraintHelperクラスで紐づけられていることがわかりました。

ただ、なぜアノテーションと実装クラスがわざわざ別のライブラリに存在しているのかがわかっていません。
BeanValidationとHibernateの関係性が関わっていそうな雰囲気はしますが、拡張性を考慮してインターフェースと実装を分けるような感覚なのでしょうか?

この点に関してご存知の方がいらっしゃいましたらご教示いただけますと幸いです。

最後までお読みいただき、ありがとうございました。

参考文献

解決のきっかけになったサイト

NotBlankValidator.java

ConstraintHelper.java


  1. jakarta.validation-api-2.0.1 

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

【Java】MapのputIfAbsentメソッドをIntelliJ IDEAに教えてもらった話

背景

既存のレガシーコードを改善していく過程で、IntelliJからの警告を減らすようにしていました。

その際に「Mapに対してkeyの値がnullだった場合、値を設定する」という処理があったのですが、それに対するIntelliJからの指摘が勉強になったので、今回記事にしました。

具体例(before)

例えば以下のような都道府県名と県庁所在地を持つMapが存在していたとします。

Map<String, String> prefectures = new HashMap<>();
prefectures.put("茨城県", "水戸");
prefectures.put("栃木県", "宇都宮");
prefectures.put("群馬県", null);

現在、key群馬県はvalueがnullになっており、ここに値を設定したいとします。
この時既存の実装では以下のような方法で値を設定していました。

if (prefectures.get("群馬県") == null) {
    prefectures.put("群馬県", "前橋");
}

この方法に対してIntelliJはputIfAbsentメソッドでよりシンプルに書けると提案してくれました。

具体例(After)

putIfAbsentメソッドはまさに上記のようなkeyがnullだった場合に値を設定してくれるメソッドで、このメソッドを用いると、先ほどの例のようなif文はなくなり、

prefectures.putIfAbsent("群馬県", "前橋");

と1行でスッキリ書くことができます。

このメソッドの存在を知らなかったので、簡単ではありますが、こうして今回記事にしました。

まとめ

「Mapに対してkeyの値がnullだった場合、値を設定する」という処理をする場合、if文を使わなくてもputIfAbsentメソッドを用いればスッキリと書くことができます。

今回このような提案までしてくれるIntelliJは凄いと改めて思いましたが、こうした便利なメソッドをどのように知れば良いのだろうかとも思いました。

「JavaDocで調べるついでに眺めてみる」、「JDKのリリースノートを読む」以外に良い方法があれば、ご教示いただけますと幸いです。

少しでもこの記事が誰かの役に立てば幸いです。最後までお読みいただきありがとうございました。

参考文献

putIfAbsentメソッドのJavaDoc

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

【Java】文字列から特定の文字を消す方法

はじめに

タイトルのとおり、業務において文字列から特定の文字を消す必要がありました。

その処理をどのように行ったのかを簡単ではありますが記事にしました。

実装例

今回{}で括られている文字列から{}を消したいとします。

この時下記のようにStringクラスのreplaceメソッドを用いれば、{}を消すことができます。

sample.java
public class Main {
    public static void main(String[] args) throws Exception {
        String test = "{test}";

        String result = test.replace("{", "").replace("}", "");

        System.out.println(result); // test が出力される

        System.out.println(result.length()); // 4 が出力される
    }
}

replaceの第一引数に消したい文字を指定し、第二引数に空文字を渡すことで特定の文字の消去を実現しています。

まとめ

文字列から特定の文字を消すにはStringクラスのreplaceメソッドを用い、第一引数に消したい文字列、第二引数に空文字を渡すことで、実現することができます。

よりスマートな方法があれば、ご教示いただけますと幸いです。

この記事が少しでも誰かの役に立てれば幸いです。
最後までお読みいただきありがとうございました。

参考文献

replaceメソッドのJavaDoc

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

【考察】System.out(in, err)は何故定数なのにnullで初期化をされているのか

新卒1年目、まだまだ研修に追われる日々が続いています。
タイトル通り何故System.out定数はnullで初期化をされているのに呼び出したら普通に使えてしまうのか
そんな疑問が浮かんでしまい、考察(今回はout定数で。他も同じような実装がされていると思うので)を
してみたので記事にしてみました。

私はjavaをそこまでかじったことがなく、基礎ができるレベルの人間です。
生半可な知識で考察してますので、間違っていましたら温かい目で読み続けて頂ければ幸いでございます。
(知ってる人がいましたらコメントいただけると嬉しいです。)

ちなみに現時点での私の環境はJDK14ですが他のバージョンも見ればもしかしたらつきとめられるのでは
と思いJDK8も同時並行で参照しております。

経緯

あまり経緯については薄っぺらいのでそこまで話すものでもないですが、
そもそも何故こんな疑問を持ったのかを最初に軽く説明させていただきます。

約半月程前に研修先の講師の方がSystemクラスの中身を見せていただいたことありました。
public static final で宣言しているにも関わらず、nullで初期化をされているというお話を
伺い、「理由はわからない(研修についてくださった講師の方も普段からjava書いていたわけでもなく
そこまで意識していなかった?)ので何方か理由が分かりましたら教えてください。」という
何気ない話題ががきっかけです。

それでは私が考察してきた物を書いていきます。

最初の考察

正直序盤から難解で諦めようかと思いました。

調べてみると同じような疑問を持っている方がチラホラいまして、最初にヒットしたサイトがこちら
「System.outの深淵」
https://kappuccino-2.hatenadiary.org/entry/20080731/1217480162

正直記事が古すぎてあまり参考にならず(特にnullPrintStream()とかどこに書いてあるんだよ!!と思ってしまいました)。
しかし、最後の方にnaitiveメソッドの事が軽く触れられており、割とヒントになるのでは?と思い
私はそれらしいout定数を操作しているそれらしいnativeメソッドの記述をSystemクラスから探してみることにしました。

とりあえず一番最初に関係していそうなのがこちら

private static native void setOut0(PrintStream out);

引数にPrintStreamクラスを取っているので明らかにこいつですね...
こちらprivate メソッドなのでクラス内部で絶対使われていると思い、調べてみました。

System.java
public class System {
    //省略
    private static void initPhase1() {
        //省略
        FileInputStream fdIn = new FileInputStream(FileDescriptor.in);
        FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);
        FileOutputStream fdErr = new FileOutputStream(FileDescriptor.err);
        setIn0(new BufferedInputStream(fdIn));
        setOut0(newPrintStream(fdOut, props.getProperty("sun.stdout.encoding")));
        setErr0(newPrintStream(fdErr, props.getProperty("sun.stderr.encoding")));
        //省略
    }
}

setOut0にFileOutPutStreamを引数にして呼び出してる!!!!(ちなみにJDK1.8ではinitializeSystemClass()メソッド
に記述があります。)
でもここでまた一つ疑問が生まれてきました。
特に代入している素振りはないし、そもそもprivateなのにどこも呼び出し先が無いのです。

振り出しに戻ってしまったと思ったのですが、メソッドの上の方に「// The charset is initialized in System.c and does
not depend on the Properties.」という記述がありました。

正直Google翻訳ではなんとも意味が...という感じでしたが、どうやらC言語で書かれているコードを
どうにかして読み込んで初期化しているのかなと思い次はcソースを探す旅に出ました。

が、その前に一つ。
実はpublic static void setOut(PrintStream out)内でもsetOut0()メソッドが使われており、
publicなので私たちが実際に呼ぶことが可能です。
なので実際に呼び出してみました。

Main.java
import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.nio.file.Files;

public class Main {
    public static void main(String[] args) {
        try (var out = new PrintStream(new FileOutputStream(new File("hello.txt")))) {
            System.setOut(out);
            System.out.println("Hello, World");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

実際にこのコードを実行してみるとソースと同じディレクトリにhello.txtが作成され、System.out.println()内の文字列が
出力されます。

これは明らかに定数は書き換えられないという私の中の常識を覆しているものであり、素直にビックリです。
このことからほかの要因でフィールド定数を初期化していると確信を持ちました。

別の要因を考えてみる

正直言ってこんあことせずとも定数宣言時に

public static final PrintStream out = newPrintStream(fdOut, props.getProperty("sun.stdout.encoding"));

と書けばよいのでは思いましたが、どうもそんな単純なことではないらしい。
正直このあたりから知識が乏しすぎて考えがうまくまとまっておりません。

SystemクラスにこのようなPrintStreamを生成しているメソッドがあります。

System.java
public class System {
    //省略
    private static PrintStream newPrintStream(FileOutputStream fos, String enc) {
       if (enc != null) {
            try {
                return new PrintStream(new BufferedOutputStream(fos, 128), true, enc);
            } catch (UnsupportedEncodingException uee) {}
        }
        return new PrintStream(new BufferedOutputStream(fos, 128), true);
    }
}

引数を見る限りtry-catchが必要になってくるコンストラクタを呼び出しております。
(他のコンストラクタならいけそうな気もしますがこのコンストラクタでなければいけない理由があるのでしょうきっと...)

ご存知の方も多いと思いますが、try-catchは式で囲みつつ代入なんていうことはできません。
ならstaticイニシャライザならいけるのでは?と思いました。

Main.java
public class Main {

    public static final Main test;

    static {
        try {
            Main a = new Main();
            test = a;
        } catch (Exception e) {
            //TODO: handle exception
        }
    }

    public Main() throws Exception {
        test();
    }

    private void test() {
        System.out.println("test");
    }

    public static void main(String[] args) {
    }
}

見返してみて思いましたがひどいコードですね...
正直頭が混乱してきて思いつかなかったので許してください...

ちなみにこちらを実行してみると、以下のようなコンパイルエラーが出ます。

Main.java:16: エラー: 変数testは初期化されていない可能性があります
    }
    ^
エラー1個

恐らく定数は直接初期化せよということなのでしょう(きっと)。

ちなみにこれを書く前にどうにかstaticイニシャライザで初期化できないかなんて考えて調べておりましたが、
以下の記事にたどり着きました。
「staticイニシャライザで例外出したらもうそのクラスは使えないよ。」
http://java.ynworks.com/archives/46

仮にできたとしてもエラーがズルズル出てきたら困るしこんなの最初に初期化されるであろうSystemクラスで記述していたら
Java使い物にならなくなってしまうかもしれませんね。

ちなみに、staticの挙動なんかも調べてみましたが、私の頭では理解が追いつかなかったです。
一応参考リンク貼っておきます。

「staticフィールドは、意図せず初期化されることがある」
https://mrtry.hatenablog.jp/entry/2017/10/06/103329
「【レポート】Javaのプログラムはどうやって動いているの?」
https://qiita.com/Hoshito/items/fdc146e4e2802d587a02

ネイティブメソッドの呼び出し先を覗いてみる。

話は戻り、nativeメソッドへ。
いろいろ頑張って考えてみましたが、やはりほかの言語で実装されているコードを見るほうがよさそうなので探してみました。

ちなみにjavaからC言語などを呼び出すシステムが標準で備わっているみたいで、「JNI」というものらしいです。
https://ja.wikipedia.org/wiki/Java_Native_Interface

まずはソースを探すところからと思ったのですが、そもそもヘッダーファイルは「jdk-14\include」にいくつかありあすが
肝心な実装部分はどこにあるのか全くわかりません。
やむなくネットでソースを探してきました。
JDK6なので結構古いコードだと思いますが、実装部分に関しては恐らく差はないと思います。
http://hg.openjdk.java.net/jdk6/jdk6/jdk/file/2f21faa922fe/src/share/native/java/lang/System.c

JNIEXPORT void JNICALL
Java_java_lang_System_setOut0(JNIEnv *env, jclass cla, jobject stream)
{
    jfieldID fid =
        (*env)->GetStaticFieldID(env,cla,"out","Ljava/io/PrintStream;");
    if (fid == 0)
        return;
    (*env)->SetStaticObjectField(env,cla,fid,stream);
}

抜粋したコードがこちら
正直何やってるのか理解に苦しんでおりますが、Cでクラスを読み込み、流してあげているのでしょう。

ちなみにJavaでもリフレクションを用いれば定数の値変更は可能みたいです。
がしかし、調べてわかったことですが、どうやらSecurityManagerというものに謎の力が加わり、
変更ができないようです。
参考(補足の部分):「Javaでprivate static finalなフィールドを動的に変更する」
https://qiita.com/5at00001040/items/83bd7ea85d0f545ae7c3

なのでC言語でnullを置き換えているといったところでしょうか。

そして最後にnullが入っている理由ですが、これも上記の補足部分に書いてある様にインライン化されている
定数は値が変更できないようで、これはSystem.outの参照している型を変更できなくなってしまうから
nullなのではないのかと勝手に自分の中で納得してしまいました。

追記

↑と勝手にnullが入っている理由を書きましたが、よく考えたら内部的にはnativeメソッドを実行しているので
関係なかったですね(^_^;)

おわり

というのが私の考察です。
正直システムの内部まで知る必要はないと思っていますが、ここまで私の単純な疑問の考察にお付き合い
いただきありがとうございました。
正直言うと考察レベル(読み返してみるとほんとに考察してるのか?と自身がみるみるなくなってきております。)
で止まってしまっているので、全然腑に落ちておりません。
しかし、気になりすぎて休みを削って調べていましたが、結構楽しいですね。
これから少し時間が取れたらJNIやJVMが起動する流れなどを調べて見ようと思っております。

もし、この手の話題を知っている方がおりましたらコメントにてお教え頂けると個人的に助かります。

ありがとうございました。

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

【Java / kotlin】改・DiscordのBotの作り方 ~あいさつをするコマンドを実装してみよう~

結構前に「DiscordのBotの作り方(Java)」と題してDiscord Botの作り方を紹介したわけですが、ライブラリのサポートが切れてしまって現在はその記事を参考にしてはBotを作れなくなってしまっているようです。

自宅学習やオンライン授業を強いられている今日この頃、Discordの需要がじわじわと高まっているようですが、それをさらに便利にするための「Bot」の作り方を紹介していきます。

この記事ではJavaとkotlin、二通りでの書き方を紹介していこうと思います!

環境

  • Javaバージョン:Java 8(JDK 1.8.0_181)
  • kotlinバージョン:kotlin 1.3.71
  • プロジェクト管理:Maven

ライブラリを調達

以下の内容をpom.xmlの<dependencies></dependencies>の中に書き入れてください。

        <!-- メインのライブラリ(必須) -->
        <dependency>
            <groupId>net.dv8tion</groupId>
            <artifactId>JDA</artifactId>
            <version>4.1.1_101</version>
        </dependency>

        <!-- コマンド処理用のライブラリ(必須) -->
        <dependency>
            <groupId>com.jagrosh</groupId>
            <artifactId>jda-utilities</artifactId>
            <version>3.0.3</version>
            <scope>compile</scope>
            <type>pom</type>
        </dependency>

        <!-- kotlinを使うならこの二つも -->
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-stdlib-jdk8</artifactId>
            <version>1.3.72</version>
        </dependency>
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-test</artifactId>
            <version>1.3.72</version>
            <scope>test</scope>
        </dependency>

Botアカウントを作成・サーバーに招待

説明が長くなってしまうので、自分の記事(こちら)を参考にトークンを取得し、サーバーに招待してください。

ログイン・認証部分

とにかくBotとしてログインしないと何も始まらないので、先ほど取得したトークンを使ってDiscordにログインしてみましょう!

以下のコードを実行して、問題がなければBotがオンラインになります。

Java

Main.java
package dev.itsu.discordbot;

import com.jagrosh.jdautilities.command.CommandClientBuilder;
import com.jagrosh.jdautilities.command.CommandClient;
import net.dv8tion.jda.api.AccountType;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.JDABuilder;
import net.dv8tion.jda.api.OnlineStatus;
import net.dv8tion.jda.api.entities.Activity;

class Main {

    private static JDA jda;
    private static final String TOKEN = "MY_TOKEN"; // 取得したBotのトークン
    private static final String COMMAND_PREFIX = "!"; // コマンドの接頭辞

    public static void main(String args[]) {
        // コマンドを扱うイベントリスナを生成
        CommandClient commandClient = new CommandClientBuilder()
            .setPrefix(COMMAND_PREFIX) // コマンドの接頭辞
            .setStatus(OnlineStatus.ONLINE) // オンラインステータスの設定
            .setActivity(Activity.watching("YouTube") // ステータスの設定(視聴中、プレイ中など)
            .build();

        jda = new JDABuilder(AccountType.BOT)
            .setToken(TOKEN) // トークンを設定
            .addEventListeners(commandClient) // commandClientを設定
            .build();
    }

}

kotlin

Main.kt
package dev.itsu.discordbot

import com.jagrosh.jdautilities.command.CommandClientBuilder
import net.dv8tion.jda.api.AccountType
import net.dv8tion.jda.api.JDA
import net.dv8tion.jda.api.JDABuilder
import net.dv8tion.jda.api.OnlineStatus
import net.dv8tion.jda.api.entities.Activity

private lateinit var jda: JDA
private const val TOKEN = "MY_TOKEN" // 取得したBotのトークン
private const val COMMAND_PREFIX = "!" // コマンドの接頭辞

fun main() {
        // コマンドを扱うイベントリスナを生成
        val commandClient = CommandClientBuilder()
                .setPrefix(COMMAND_PREFIX) // コマンドの接頭辞
                .setStatus(OnlineStatus.ONLINE) // オンラインステータスの設定
                .setActivity(Activity.watching("YouTube")) // ステータスの設定(視聴中、プレイ中など)
                .build()

        jda = JDABuilder(AccountType.BOT)
                .setToken(TOKEN) // トークンを設定
                .addEventListeners(commandClient) // commandClientを設定
                .build()
}

ステータスの変更

オンラインステータスに設定できる値

OnlineStatus.ONLINE // オンライン
OnlineStatus.OFFLINE // オフライン
OnlineStatus.IDLE // 退席中
OnlineStatus.DO_NOT_DISTURB // 取り込み中
OnlineStatus.INVISIBLE // オンライン状態を隠す

ステータスに設定できる値

Activity.watching("name") // nameを視聴中
Activity.listening("name") // nameを再生中
Activity.streaming("name", "url") // nameを配信中
Activity.playing("name") // nameをプレイ中

コマンドを使ってみる

今回は試しに、「コマンドを実行した人にあいさつをする」コマンドを実装してみようと思います。

コマンドを実装する

コマンドを実装するには、om.jagrosh.jdautilities.command.Command を継承したクラスを作る必要があります。

Java

HelloCommand.java
package dev.itsu.discordbot

import com.jagrosh.jdautilities.command.Command;
import com.jagrosh.jdautilities.command.CommandEvent;

class HelloCommand extends Command {

    public HelloCommand() {
        this.name = "hello"; // コマンド名を設定(!helloで実行可能)
        this.help = "あいさつするだけ"; // コマンドの説明(!helpと実行したときに表示される説明)
    }

    // コマンドを実行したときに呼ばれるメソッド
    @Override
    public void execute(CommandEvent event) {
        event.reply("こんにちは、" + event.getAuthor().getName() + "さん!"); // 返信
    }

}

kotlin

HelloCommand.kt
package dev.itsu.discordbot

import com.jagrosh.jdautilities.command.Command
import com.jagrosh.jdautilities.command.CommandEvent

class HelloCommand : Command() {

    init {
        name = "hello" // コマンド名を設定(!helloで実行可能)
        help = "あいさつするだけ" // コマンドの説明(!helpと実行したときに表示される説明)
    }

    // コマンドを実行したときに呼ばれる関数
    override fun execute(event: CommandEvent) {
        event.reply("こんにちは、${event.author.name}さん!") // 返信
    }

}

コマンドを登録する

クラスを作っただけではまだコマンドを実行できません。
コマンドを実行できるようにするためには、実装したコマンドのクラスをCommandClientに登録してあげる必要があります。
先ほどのMain.java(Main.kt)に戻って、以下のように変更してください。

これを実行すれば、Discord内で「!hello」と実行したときに「こんにちは、(ユーザー名)さん!」とBotが返してくれます!

Java

Main.java
package dev.itsu.discordbot;

import com.jagrosh.jdautilities.command.CommandClientBuilder;
import com.jagrosh.jdautilities.command.CommandClient;
import net.dv8tion.jda.api.AccountType;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.JDABuilder;
import net.dv8tion.jda.api.OnlineStatus;
import net.dv8tion.jda.api.entities.Activity;

class Main {

    private static JDA jda;
    private static final String TOKEN = "MY_TOKEN";
    private static final String COMMAND_PREFIX = "!";

    public static void main(String args[]) {
        CommandClient commandClient = new CommandClientBuilder()
            .setPrefix(COMMAND_PREFIX)
            .setStatus(OnlineStatus.ONLINE)
            .setActivity(Activity.watching("YouTube")
            .addCommands(HelloCommand()) // ここでコマンドを登録!
            .build();

        jda = new JDABuilder(AccountType.BOT)
            .setToken(TOKEN)
            .addEventListeners(commandClient)
            .build();
    }

}

kotlin

Main.kt
package dev.itsu.discordbot

import com.jagrosh.jdautilities.command.CommandClientBuilder
import net.dv8tion.jda.api.AccountType
import net.dv8tion.jda.api.JDA
import net.dv8tion.jda.api.JDABuilder
import net.dv8tion.jda.api.OnlineStatus
import net.dv8tion.jda.api.entities.Activity

private lateinit var jda: JDA
private const val TOKEN = "MY_TOKEN"
private const val COMMAND_PREFIX = "!"

fun main() {
        val commandClient = CommandClientBuilder()
                .setPrefix(COMMAND_PREFIX)
                .setStatus(OnlineStatus.ONLINE)
                .setActivity(Activity.watching("YouTube"))
                .addCommands(HelloCommand()) // ここでコマンドを登録!
                .build()

        jda = JDABuilder(AccountType.BOT)
                .setToken(TOKEN)
                .addEventListeners(commandClient)
                .build()
}

実はaddCommands()メソッド(関数)は可変長引数を取るので、以下のようにほかに実装したコマンドをいくつでも登録することができます。

addCommands(HelloCommand(), StatusCommand())

ほかに紹介してほしい機能があったり、ご指摘等がある場合はコメントにお願いします。

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