20201026のJavaに関する記事は9件です。

GCP: FunctionsとPub/Subを連携させる

前置き

Google Cloud Functionsから別のFunctionsを呼び出す方法として、Pub/Subを使うやり方を試しました。

構成

  • Google Cloud Platform
    • Pub/Sub
    • Cloud Functions (Java11) image.png

やってみる

Pub/Subとは

イベント ドリブン システムとストリーミング分析のためにメッセージングと取り込みを行います。
Cloud Pub/Sub  |  Google Cloud

Pub/Sub は、イベントを処理するサービスとイベントを生成するサービスを切り離す非同期メッセージング サービスです。
Pub/Sub とは  |  Cloud Pub/Sub ドキュメント  |  Google Cloud

用語と登場人物の関係は公式ドキュメントが分かりやすいです。
パブリッシャーとサブスクライバーの関係

大まかな流れは下記のとおりです。
1. 事前にトピックを作成
2. パブリッシャー(いわゆる呼び出し元)がトピックにメッセージ(データ)を作成(publish)
3. サブスクライバー(いわゆる呼び出し先)によってサブスクリプションが作成され、メッセージを受信
4. メッセージがすべて受信されると、サブスクリプションが終了

パブリッシャーとサブスクライバーは、一対一ではなく一対多や多対多など自由に設定できます。

GCPで動かす

大まかな手順

  1. Pub/Subにトピックを作成する
  2. FunctionsにPub/Subトリガーを作成する
  3. トピックにメッセージをpublishするFunctionsを作成する

3のHTTPトリガー -> 1のトピック -> 2のFunctions
というフローになります。

1. Pub/Subにトピックを作成する

GCPコンソール > Pub/Sub > トピックから「トピックを作成」します。
トピックIDは任意の文字列を設定します。

2. FunctionsにPub/Subトリガーを作成する

GCPコンソール > Cloud Functionsから「関数を作成」します。
トリガータイプCloud Pub/Subにし、1 で作成したトピックを選択し、構成を保存します。

コードのランタイムは好きなものを選んでください。今回はJava11です。

サブスクライブ(Pub/Sub -> Functions)の検証

ここまで設定したら、1 のPub/Subトピックに戻ってトリガーされることを確認します。
GCPコンソール > Pub/Sub > トピックで対象のトピックを開き「メッセージをパブリッシュ」します。
確認したいだけなので「1つのメッセージを公開」で「1回」だけメッセージを追加します。

GCPコンソール > Cloud Functions で対象の関数の「ログ」の開きます。
うまくいっていれば、このようにパブリッシュしたメッセージが表示されます。
image.png

3. トピックにメッセージをpublishするFunctionsを作成する

公式ドキュメントのソースを参考に、gradleで作成しました。
トピックへのメッセージのパブリッシュ  |  Cloud Pub/Sub  |  Google Cloud

PublishFunction.java
import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.HttpRequest;
import com.google.cloud.functions.HttpResponse;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.protobuf.ByteString;
import com.google.cloud.pubsub.v1.Publisher;
import com.google.pubsub.v1.ProjectTopicName;
import com.google.pubsub.v1.PubsubMessage;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ExecutionException;

public class PublishFunction implements HttpFunction {
    private static final String PROJECT_ID = System.getenv("GOOGLE_CLOUD_PROJECT");
    private static final String TOPIC_NAME = System.getenv("GOOGLE_CLOUD_PUBSUB_TOPIC");

    @Override
    public void service(HttpRequest request, HttpResponse response) throws Exception {
        String message = request.getFirstQueryParameter("message").get();
        ByteString byteStr = ByteString.copyFrom(message, StandardCharsets.UTF_8);
        PubsubMessage pubsubApiMessage = PubsubMessage.newBuilder().setData(byteStr).build();

        try {
            Publisher publisher = Publisher.newBuilder(ProjectTopicName.of(PROJECT_ID, TOPIC_NAME)).build();
            try {
                publisher.publish(pubsubApiMessage).get();
                response.setStatusCode(200);
                response.getWriter().write(message);
            } finally {
                publisher.shutdown();
                publisher.awaitTermination(1, TimeUnit.MINUTES);
            }
        } catch (InterruptedException | ExecutionException e) {
            System.out.println("Error publishing Pub/Sub message: " + e.getMessage());
            response.setStatusCode(500);
            response.getWriter().write(message);
        }
    }
}

下記を設定

build.gradle
dependencies {
    implementation platform("com.google.cloud:libraries-bom:5.3.0");
    implementation("com.google.cloud:google-cloud-pubsub");
    implementation('com.google.protobuf:protobuf-java:3.13.0')
}

作ったソースをFunctionsに登録します(Cloud Buildからデプロイしました)。
トリガータイプはHTTPにしておきます。

また、ランタイム変数GOOGLE_CLOUD_PROJECTを設定する必要があります。
TOPIC_NAMEの方はコードに直接記述してもOKですが、今回はランタイム変数として設定しました。
環境変数の使用  |  Google Cloud Functions に関するドキュメント

パブリッシュ(Functions -> Pub/Sub)の検証

3 で作った関数のHTTPトリガーをコールします。
URLパラメータmessageを渡せるようにしているので、ここに任意の文字を設定します。
これが1 のPub/Subにメッセージとしてパブリッシュされ、最終的に 2 の関数に出力されれば成功です。

ローカルで試す

ローカルでGOOGLE_APPLICATION_CREDENTIALSの環境変数を設定しても、下記の例外が発生します。
ここはうまくやるやり方が分かっていません :thinking:

java.io.IOException: The Application Default Credentials are not available. 
They are available if running in Google Compute Engine. 
Otherwise, the environment variable GOOGLE_APPLICATION_CREDENTIALS must be defined pointing to a file defining the credentials. 
See https://developers.google.com/accounts/docs/application-default-credentials for more information.

最後に

Functionsを連携させるのに何がいいかなと探して、Pub/Subにたどり着きました。
Functionsは起動時間に対して課金されるので、非同期で処理できるのは都合がよさそうです。

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

【Java】Spring DIの仕組み

Spring DIコンテナの機能

  • アプリケーションの起動時にエントリポイントが必要
  • JavaではMainメソッドが起動するエントリポイントになる
  • SpringBootではMainメソッドのクラスに@SpringBootApplicationがついている
  • Spring ランタイムは起動時にこのアノテーションがついているパッケージ下にある全クラスをチェック
  • @Bean(及びその派生クラス, @Component, @RestController, @Serviceなど)を 自動的に検出しSpring DIコンテナに登録
  • 検出したメソッドに@GetMappingなどがついていたらここをエンドポイントとして、”/“のURL(=8080)にリスニング
  • そこから来たリクエストをURLの内容に応じて登録したコントローラに自動で分けてくれる
  • Spring Web が選択すると、DIコンテナに登録されたBean から @RestController が付与されたクラスを探して、@GetMapping などが付与されたメソッドをエンドポイントにする

DI.png

ルーティングの仕組みについて

  • 以下の例ではDemoApplication、SampleApplicationはBeanとして登録され2つともルーティング対象になる
Project Root
└─src
    └─ main
        └─ java  
            └─ com.example
                └─ demo
                    └─DemoApplication
                    └─SampleApplication

失敗例

  • DemoApplication、SampleApplication共に”/”を指定した場合、SampleApplicationが優先される。

結果.png

DemoApplicaton.java
package com.example.demo;

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

@SpringBootApplication
@RestController
public class DemoApplication {

    @RequestMapping("/") //どこのアドレスからとってくるか、”/“はhttp://localhost:8080/を指す
    String index(){
        return "Hello World!";
    }
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}
SampleApplicaton.java
package com.example.hoge;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class SampleController {
    @RequestMapping(value = "/", method = RequestMethod.GET)
    //URLのrootに対してリクエストが来たら受け取る
    //DemoApplication とルート(=8080)が被っている
    public String index(Model model) {
        model.addAttribute("message", "Hello World!!");
        return "index";
    }
}

ルーティングの仕組み

  • @SpringBootApplicationがcom.example.demo以下にある場合、demoより下のパッケージを読みに行く
  • SampleController.javaをcom.example.hogeに移動→SampleControllerはルーティング対象外になる!
Project Root
└─src
    └─ main
        └─ java  
            └─ com.example
                └─ demo
                    └─DemoApplication
                └─ hoge
                    └─SampleApplication
DemoApplication.java
package com.example.demo;

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

@SpringBootApplication
@RestController
public class DemoApplication {

    @RequestMapping("/hoge")
    String index(){
        return "Hello World!";
    }
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}
  • SampleControllerの”/”を”/bar”に変更
  • http://localhost:8080/bar にアクセス
  • @SpringBootApplicationがパッケージ下にある全クラスをチェックできていない!DIコンテナに登録できていない! > Whitelabel Error Page

err.png

SampleApplicaton.java
package com.example.hoge;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class SampleController {
    @RequestMapping(value = "/bar", method = RequestMethod.GET)
    public String index(Model model) {
        model.addAttribute("message", "Hello World!!");
        return "index";
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

インスタンスをインポートして別画面で使う

ちょっと躓いたのでメモ

超基本的な事と思うけど、PHPには無かった事

クラスのインスタンス化。

これは至る所で紹介されているので簡単に見つけれた。

//顧客情報のインスタンス化
Customer customer = new Customer();

こんな感じでnew してやればインスタンス化できる。

しかし画面Aでインスタンス化したものを画面Bでnewせずに使うにはどうすればよいか?

迷ったけどやってみたら結構簡単にできました。

画面A(Java)でsetAttributeして

request.setAttribute("customer", customer);

画面B(JSP)でインポートして受け取る

<%@ page import="pizzaOrderForm.customer.*" %>
Customer customer = (Customer)request.getAttribute("customer");

これでインスタンス化したcustomerを別画面でも使えるようになりました。

Java道は難しいな…

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

[Java] 配列からランダムに値を取得する

Collections.shuffleを使用

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


List<String> list = Arrays.asList("AA", "BB", "CC");
System.out.println(list); // =>  [AA, BB, CC]

Collections.shuffle(list);
System.out.println(list);  // => [CC, AA, BB]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Apache POI ExcelをKotlinで

現在のApache POI Excel公式ドキュメント(https://poi.apache.org/components/spreadsheet/index.html) が新旧メソッドが混ざって解説されているなど、分かりづらい部分が多かったためこちらによく使う記述をまとめました。
言語 kotlin

セットアップ

gradlle インストール
build.gradle
dependencies {
/* 2020 8月現在の安定版4.1.2*/

 'ApachePOI'
  compile("org.apache.poi:poi:4.1.2")

  'xlsxファイルを作成する場合は下記poi-ooxmlが必要'
  compile("org.apache.poi:poi-ooxml:4.1.2")

}

基本操作 

シートの作成
sample.kt
// XSSFWorkbookエンティティを作成
val workBook = XSSFWorkbook()

// エクセルシートを作成
val sheet = workBook.createSheet()
指定セルに値を記入
sample.kt
// 値を記入するセルを指定する

// createRowで列を指定
val row = sheet.createRow(0)

// createCellで行を指定
val cell = row.createCell(0)

// 値を記入
cell.setCellValue("テスト")
シートの保存
sample.kt
val fileOutputStream = FileOutputStream("test.xlsx")

workBook.write(fileOutputStream)

fileOutputStream.close()
結果

スクリーンショット 2020-08-15 22.44.33.png

応用操作


セルの色指定
sample.kt
// スタイルインスタンスを作成
val style = workBook.createCellStyle()
// セルを黄色に設定
style.fillForegroundColor = (XSSFColor(byteArrayOf(255.toByte(), 255.toByte(), 204.toByte()), null)
// 塗りつぶしを指定
style.fillPattern = FillPatternType.SOLID_FOREGROUND
//cellにスタイルを設定
cell.setCellStyle(style)
結果

スクリーンショット 2020-08-16 16.25.52.png


フォントの設定
sample.kt
// フォントインスタンスを作成
val font = workBook.createFont()
// 文字サイズを設定
font.setFontHeightInPoints(16.toShort())
// 文字種類を設定
font.fontName = "MS Pゴシック"

val style = workBook.createCellStyle()
// スタイルにフォント情報を追加
style.setFont(font)


条件付き書式の設定

設定できる条件の一覧
https://github.com/apache/poi/blob/trunk/src/java/org/apache/poi/ss/usermodel/ComparisonOperator.java

sample.kt
val sheet = workBook.createSheet()

// 条件付き書式設定用のインスタンスを生成
val sheetCF: SheetConditionalFormatting = sheet.sheetConditionalFormatting

// 条件を設定 この場合は値が90%以下である事
val rule1 = sheetCF.createConditionalFormattingRule(ComparisonOperator.LT, "90%")

// 上記条件の時に利用するフォントを設定(フォント以外も様々な要素を設定できる)
val font = rule1.createFontFormatting()
// フォントを赤に設定
font.fontColor = XSSFColor(byteArrayOf(255.toByte(), 0.toByte(), 0.toByte()), null)

// 上記条件が有効なセルの範囲を設定(下記の場合はA1セルからA5セルまで)
val range = "A1:A5"

// 範囲は配列内に複数設定できる
val regions = arrayOf(
      CellRangeAddress.valueOf(range)
    )

// 条件を有効化
sheetCF.addConditionalFormatting(regions, rule1)


セルに数式を設定
sample.kt
val sheet = workBook.createSheet()
val row = sheet.createRow(0)
val cell = row.createCell(0)

// A1セル/C1セルの割合を出す数式を作成
val formula = "A1/C1"

// セルに数式を設定
cell.cellFormula = formula

// 数式を有効化する
sheet.setForceFormulaRecalculation(true)

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

Java関連自分用メモ

2021/01/26: そろそろ増えてきたので、項目ごとを記事ごとに分けようかな

時間関連

Dateを使わない、outdated、下の参照

    public static Date endYearDate(String dateStr) {
        return DateUtils.parseDate(String.format("%s/12/31", dateStr), "yyyy/MM/dd");
    }

    public static Date startYearDate(String dateStr){
        return DateUtils.parseDate(String.format("%s/01/01", dateStr), "yyyy/MM/dd");
    }

    public static Date startMonthDate(String dateStr) {
        LocalDate localDate = LocalDate.parse(dateStr, DateTimeFormatter.ofPattern("yyyy/MM/dd"));
        return Date.from(localDate.withDayOfMonth(1).atStartOfDay(ZoneId.systemDefault()).toInstant());
    }

    public static Date endMonthDate(String dateStr) {
        LocalDate localDate = LocalDate.parse(dateStr, DateTimeFormatter.ofPattern("yyyy/MM/dd"));
        return Date.from(localDate.withDayOfMonth(localDate.lengthOfMonth()).atStartOfDay(ZoneId.systemDefault()).toInstant());
    }

java.sql.Timestampとjava.sql.Dateはoutdated

使うなら java8 のjava.timeを使う(Instant, LocalDateTime)

Instant now = Instant.now();
Instant instant = myJavaSqlTimestamp.toInstant();

//秒までだけ欲しい場合
LocalDateTime localDateTimeNow = LocalDateTime.now().truncatedTo(ChronoUnit.SECONDS)

参考リンク

https://stackoverflow.com/questions/8929242/compare-date-object-with-a-timestamp-in-java
https://www.baeldung.com/migrating-to-java-8-date-time-api
https://stackoverflow.com/questions/21448500/how-to-remove-milliseconds-from-date-object-format-in-java
InstantとLocalDateTimeの違い
https://stackoverflow.com/questions/32437550/whats-the-difference-between-instant-and-localdatetime
Java 8 Date Time Intro
https://www.baeldung.com/java-8-date-time-intro

CSVファイル関連

Controller

@PostMapping(value = "/csv", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public ResponseResult insertCsv(@RequestPart("file") MultipartFile file){
       //処理
    }

Service

public List<DTO> convertCsvFileToList(MultipartFile file) throws Exception{
        List<DTO> csvList = new ArrayList<>();
        InputStream is = file.getInputStream();
        InputStreamReader isr = new InputStreamReader(is, "UTF-8");
        BufferedReader br = new BufferedReader(isr);

        String line;
        int cnt = 0;
        while((line = br.readLine()) != null){
        byte[] b = line.getBytes();
        line = new String(b, "UTF-8");
        String[] columns = line.split(",", -1);
        if(cnt != 0){
           //csvのcolumn数
           if(columns.length == 5){
                        String columns0 = columns[0];
                        ...

                }
                cnt++;
            }

        return csvList;
    }

Test(ファイル作成なし)

        String[] header = {"Column1", "Column2", "Col3"};
        String[] data1 = {"A", "1", "あ"};

        String joinedHeader = String.join(",", header);
        String join1 = String.join(",", data1);

        String csv = joinedHeader + "\n" + join1;

        MockMultipartFile file = new MockMultipartFile("file", "test", MediaType.MULTIPART_FORM_DATA_VALUE, csv.getBytes());

        service.convertCsvFileToList(file);

Enum

listの中でEnumのもの存在しているかどうか

EnumSet<MyEnum> set = EnumSet.allOf(MyEnum.class);
boolean contains = list.stream().anyMatch(set::contains);
参考link
```java https://stackoverflow.com/questions/49772441/check-if-list-contains-at-least-one-of-another-enums ```
## Lambda lambdaのforEachで次のアイテムに行く ```java public static void main(String[] args) { ArrayList stringList = new ArrayList<>(); stringList.add("a"); stringList.add("b"); stringList.add("c"); stringList.stream().forEach(str -> { if (str.equals("b")) return; // これのみスキップされます System.out.println(str); }); } ``` ```java //これはあくまでも自分独自のメソード扱い str -> { if (str.equals("b")) return; System.out.println(str); } ``` ```java //これもただのloopに過ぎない stringList.stream().forEach() ``` なので上の動きは細かいコードにするとこんな感じ ```java public static void main(String[] args) { ArrayList stringList = new ArrayList<>(); stringList.add("a"); stringList.add("b"); stringList.add("c"); for(String s : stringList) { lambdaExpressionEquivalent(s); } } private static void lambdaExpressionEquivalent(String str) { if (str.equals("b")) { return; } System.out.println(str); } ```
参考link https://stackoverflow.com/questions/32654929/move-to-next-item-using-java-8-foreach-loop-in-stream

List関連

Listの中で複数ものが存在しているかどうかをチェック

*List.containsAllもよいが、O(NK)なので、performanceが落ちてしまう、hashsetを使えばperformanceが上がります

HashSet<String>set = new HashSet<>(list);
   if(set.containsAll(Arrays.asList("asd", "efg"))){
   //何かをやる
}

Listから物を削除

ArrayList<String> myList = new ArrayList<>();
myList.add("a");
myList.add("b");
myList.add("c");

Iterator i = myList.iterator();
while(i.hasNext()){
  String str = i.next();
  if(str.equals("b")){
    i.remove();
    break;
  }
}

for(String str : myList){
    System.out.println(str);
}
参考link 1.https://stackoverflow.com/questions/20455654/convenient-method-to-check-if-a-list-contains-multiple-elements 2.https://www.tutorialspoint.com/use-iterator-to-remove-an-element-from-a-collection-in-java#:~:text=An%20element%20can%20be%20removed,the%20exception%20IllegalStateException%20is%20thrown.
## List\>関連 ```java List> list; for (Map map : list) { for (Map.Entry entry : map.entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); } } ///もしくは list.forEach(item -> item.forEach((k, v) -> System.out.println(k + ": " + (String)v) ); ``` ##Mybatis関連 DTO ```java public class DTO { //SQLのDBではaddress private String address; //SQLのDBではcreated_time private Timestamp createdTime; } ``` Mapper ```java //DTOの名前とDBの名前をマッピング成功 @Insert("insert into table(address,created_time)" + "values(#{address},#{createdTime})") int insertSuccess(DTO dto); //addressは取得成功だが、createdTimeはマッピングされずnullになります @Select("select * from table") List getAllFail(); //こんな風にマッピングさせ、createdTimeの取得が成功します,idは何を書いてもいい @Results(id="WriteAnythingHere", value={ @Result(column="address",property="address"), @Result(column="created_time", property="createdTime")) }) @Select("select * from table") List getAllSuccess(); //@ResultMapで上記のマッピングIDを使って何度も同じものを書かずに再用できます @ResultMap("WriteAnythingHere") @SelectProvider(type = Table1Provider.class, method="findAllByCondition") List findAllByCondition(DTO dto); class Table1Provider{ public String findAllByCondition(DTO dto){ return new SQL(){{ SELECT("*"); FROM("table"); if((null != dto.getAddress()) && (0 != dto.getAddress().length())){ WHERE("address=#{address}"); } ORDER_BY("created_time"); }}.toString() + " DESC" ; } }; ``` ####参考リンク https://qiita.com/yoshikawaa/items/b694361026c3993472af ##Partition関連 ##Set関連 新しいsetを古いsetに入れる,setは重複する物がないので、そのまま気にせずaddAllすればok ```java newStringSet.addAll(oldStringSet); ``` ##Streams関連 配列で存在しているかどうか ```java String[] values = {"AB","BC","CD","AE"}; boolean contains = Arrays.stream(values).anyMatch("s"::equals); ``` primitiveではない方の探し方 ```java int[] a = {1,2,3,4}; boolean contains = IntStream.of(a).anyMatch(x -> x == 4); ``` 最後の要素をゲット ```java List myList = Arrays.asList("a", "b", "c"); String result = myList.stream().reduce((first, second) -> second).orElse("nothing"); //"c" System.out.println(result); //または myList.get(myList.size() - 1); //もしくは myList.stream.skip(myList.size() - 1).findFirst().orElse("Nothing"); ```
参考リンク https://stackoverflow.com/questions/1128723/how-do-i-determine-whether-an-array-contains-a-particular-value-in-java https://mkyong.com/java8/java-8-get-the-last-element-of-a-stream/

参考リンク

https://stackify.com/streams-guide-java-8/

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

ゲッターとセッター(Java)

ゲッターとセッター

Javaオブジェクトのフィールドに値を設定(セット)するメソッドがセッター
その値を取得(ゲット)する値がゲッター

基本

慣習として以下のような名前、引数、戻り値になっている。

メソッド名 引数 戻り値
ゲッター getXXX() 引数なし フィールドの値
セッター setXXX() 引数一つ void型

ゲッター

その前にまず、カプセル化について

プログラムを形成するオブジェクトについて、その中身を他のオブジェクトからなるべく見えなくすること。
他のクラスからそのフィールドにアクセス出来なくするために、フィールドを全てprivateにすることがカプセル化の原則。

private 型名 フィールド名();

カプセル化でprivateにしたフィールドは他のクラスから直接アクセスできない。
アクセスする必要があるときのために間接的にアクセスできる手段を用意する。
具体的には、間接的にアクセスするための以下のようなメソッドを用意する。

戻り値の型 メソッド名(){
    return フィールド名;
}

このメソッドはただフィールドの値をreturnで返すだけのメソッドとなっている。
そのため、戻り値の型はフィールドの型と同じになる。
例えば、

private int piyo;

に対しては

public int getPiyo(){
    return piyo;
}

となる。

他のクラス内で「getPiyo()」メソッドを呼び出すことにより、privateにした
int型のフィールド「piyo」の値を取得できるようになる。
要するに、ゲッターを他のクラスから呼ぶことによって、戻り値としてprivateなフィールドの値をゲットする。
これがゲッター。
こうすると、フィールドの名前や値の取得ロジックに変更があっても、「getPiyo()」メソッドの
中を書き換えるだけで済む。
また、「getPiyo()」メソッドを使っているほかのクラスの書き換えは一切行わなくて良いというメリットもある。

セッター

ゲッターにより、privateなフィールドの値をゲットできることが分かった。
しかし、ゲッターはただフィールドの値をreturnで返すだけのメソッドのため、フィールドの値を書き換えることができない。
その値を書き換えるためのメソッドがセッターである。
一般的には以下のメソッドを用意する。

戻り値の型 メソッド名(引数){
    フィールド名 = 引数;
}

このメソッドはフィールドに入力したい値を引数として受け取り、メソッド内でフィールドに代入しているだけなので、引数の型はフィールドと同じになる。
例えば

private int hoge;

に対しては

public void setPiyo(int piyo){
    piyo = hoge;
}

となる。
これはただフィールド「piyo」に引数で受け取った値「hoge」を代入しているだけである。
ゲッターと同様に、こうすることで何か変更があったとしてもクラス内の「setPiyo()」メソッドの
中を書き換えるだけで済む。
また、「serPiyo()」メソッドを使っているほかのクラスの書き換えは一切行わなくて良いというメリットもある。

アクセサメソッド

セッターやゲッターのようにpravateで隠ぺいしたフィールドへ間接的にアクセスするために用意したメソッドをアクセサメソッドと呼ぶ。
アクセサメソッドを用いてフィールドにアクセスすることで、クラス間の依存性を低め、追加・変更の作業を楽にし、自由度を高めることができる。

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

【Java】基本

Java学習中。
復習に見返せるようにメモしていきます。
ほぼ自分の勉強メモです。
過度な期待はしないでください。

出力の命令

 - プログラミングでは、コンピュータに様々な命令をし処理を行わせる。
 System.out.println() と書く事で( )の中身を出力(表示)せよ」という命令させる。

 - 文字列
 文字列はダブルクォーテーション(")で囲む。
 " "で囲んでいないと、コードが動かない。

 - セミコロン
 Javaでは文の終わりにセミコロン(;)を付けなければならない。
 これを忘れるとコードが動かない。

 - コメント
 コメントはコードが実行されるときにすべて無視される。
 なので、メモ等を残しておきたい時に使用する。
 「//」と書くことで、そこから行末までがコメントとみなされる。

// Hello World と出力させる
System.out.println("Hello World");

// 出力結果 → Hello World


 - 数値
 数値は文字列と違い、ダブルクォーテーションで囲まない。
 記号はすべて半角で記述。

// 数値の17を出力
System.out.println(17);
// 出力結果 → 17

// 5に3を足した値を出力
System.out.println(5 + 3);
// 出力結果 → 8

// 「5 + 3」を文字列として出力
System.out.println("5 + 3");
// 出力結果 → 5 + 3


 - 文字列の連結

// "こんにちは"と"世界"を連結して出力
System.out.println("こんにちは" + "世界");
// 出力結果 → こんにちは世界

// "38"と"19"を連結して出力
System.out.println("38" + "19");
// 出力結果 → 3819




変数の定義

 - 変数とは、データ(値)をいれておく為の箱。
 変数を使うには、まず変数を「定義」する必要がある。
 Javaで変数を定義するためには、
 ①変数にいれる値のデータ型を指定する
 ②変数の名前を決める
 という2つの作業が必要になる。

 - データ型
 データ型には、文字列と整数の2つの型がある。
 String型は、文字列の型、 int型は、整数の型 となる。
 String型のSは大文字、int型のiは小文字である点に注意。
 また、その他にもdouble型と言う小数を表すデータ型も存在する。

 - 値の代入
 変数を定義したら変数に値を入れる。
 「変数名 = 値」 で代入する事が出来る。

// int型の変数numberを定義
int number;

// 変数numberに3を代入
number = 3;

// 変数numberを出力
System.out.println(number);

// 出力結果 → 3



// String型の変数nameを定義
String name;

// 変数nameに"Aki"を代入
name = "Aki";

// 変数nameを出力
System.out.println(name);

// 出力結果 → Aki


 - 変数の初期化
 変数定義と同時に値を代入することを言う。

書き方
// int型の変数numberを定義し、7を代入
int number = 7;

// Stringの変数textを定義し、「おはよう」を代入
String text = "おはよう";


 - 変数の更新
 更新するときはデータ型をつけないように注意。
 これは、変数名の前にデータ型をつけると変数を定義しようとしますが、
 同じ処理内で同一名の変数を定義できない、なので更新するときはデータ型をつけない。

書き方
int number = 7;

// 変数numberを9で上書き
number = 9;

// 出力結果 → 9


 - 自己代入
 変数numberと8を足して、また変数numberに代入する、
 このような代入の仕方を自己代入と言う。

書き方
int number = 7;

// 変数numberの値に8を足して、変数numberを上書き
number = number + 8;

// 出力結果 → 15


 - 自己代入の省略
 基本形: X = X + 1; → 省略形: X += 1; → さらに省略: X++;
 基本形: X = X - 1; → 省略形: X -= 1; → さらに省略: X--;

型変換

 - 「+」などの操作は同じデータ型同士でないとできない。
 よって型の違うものを演算するときは、型を変換し同じ型にする。
 Javaではこの型変換の方法として、自動変換と手動変換がある。

 - 自動型変換
 下記のようにString型とint型を足すと、int型が自動でString型に変換され、
 文字列の結合が行われる。

int month = 12;
int date = 31;

// 「12月31日」となるように変数と文字列を連結して出力
System.out.println(month + "月" + date + "日");

// 出力結果 → 12月31日


 int型とdouble型の計算では結果はdouble型になる。

// 7を2.0で割った値を出力してください
System.out.println(7/2.0);

// 出力結果 → 3.5


// 7.0を2で割った値を出力してください
System.out.println(7.0/2);

// 出力結果 → 3.5


 - 強制型変換
 (変換したいデータ型)値 とする事で強制的に型を変換させます。

int number1 = 7;
int number2 = 2;

// number1をdouble型に変換し、number2で割った値を出力
System.out.println((double)number1 / number2);

// 出力結果 → 3.5
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Android/Java】超シンプルなOptionMenuの作成(API非実装)

はじめに

Android開発歴1週間の超ビギナーです。
アウトプットとして簡単なOptionMenuを作成してみました。
これを実装するだけでも、かなり時間かかったので同じビギナーの方の参考になれば幸いです。
※記載量を省略すべく、stringsファイルは使用しておりません。

アプリの概要

画面右上に設置した保存ボタンを押すと、表示されているメッセージが変更するという超シンプルなアプリ
 

各ファイルのコード

オプションメニューの表示レイアウト設定

1) オプションメニュー用のxmlファイルをres/menuフォルダ内に作成する。
2) オプションメニューの大枠はmenuタグ、メニューの要素(保存ボタン)はitemタグでつくる。
3) itemタグに必要な3つの属性
android:id : メニューファイルとjavaファイルを紐付けるための鍵
android:title : メニューに表示する文字列
app:showAsAction : アクションバーに表示するかどうか(never<ifRoom<always)

option_menu.xml
// 最初のデフォルト文は全て消して下記を記述
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:id="@+id/option_save"
        android:title="保存"
        app:showAsAction="always" />

</menu>

メイン画面のレイアウト設定

android:id : レイアウトファイルとjavaファイルを紐付けるための鍵
android:text : メイン画面に表示する文字列

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

   // idとtextをいじるだけ
    <TextView
        android:id="@+id/text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="保存ボタンを押してみよう"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

javaファイル 

※package, import 文は省略してます。(赤字になったらoption + Enterを押してください)

下記2つのメソッドはオプションメニューを実装するためのルールだと思ってください。
onCreateOptionsMenu(Menu menu)
onOptionsItemSelected(MenuItem item)

MainActivity.java
public class MainActivity extends AppCompatActivity {
    // デフォルトのままでok
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    // オプションメニュー(保存ボタン)の表示
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // MenuInflater(メニューをJavaオブジェクトまでインフレートさせるためのクラス)を取得
        MenuInflater inflater = getMenuInflater();
        // option_menu.xmlに記述されたメニュー部品をJavaオブジェクトにインフレートする
        inflater .inflate(R.menu.option_menu, menu);
        // 戻り値をreturnする
        return true;
    }

    // オプションメニュー(保存ボタン)が選択されたときの処理
    @Override
    public boolean onOptionsItemSelected(MenuItem item)
        // activity_main.xmlのTextViewを取得
        TextView textView = findViewById(R.id.text_view);
        // 取得したテキストに新しい文字列を設置
        textView.setText("保存ボタンが押されました");
        // 戻り値をreturnする
        return true;
    }
}

最後に

超初心者なので違うところありましたらぜひご指摘お願いします。

参考サイト

https://developer.android.com/guide/topics/ui/menus?hl=ja#options-menu
https://qiita.com/watataku/items/5faad0384b54d0c53f6e
ありがとうございました!

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