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

springbootでのアプリ作成につきまして

今、以下の機能を有するアプリの作成しています。
 ・データの入力、登録
 ・登録データの一覧表示
 ・登録データの削除
 ・登録データの曖昧検索

上記のうち、曖昧検索だけがいまだにエラーなどでできていない状態になっています。

どこをどのように修正すれば、エラーが解消し、検索機能が動くようになるかご教示頂けますでしょうか。
◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆
データベース
 sp→観光地を入れる変数(String型)

◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆

コード
//MyDataRepository.java

package com.example.demo;

import java.util.List;

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

import org.springframework.stereotype.Repository;

@Repository

public interface MyDataRepository extends JpaRepository {

List<MyData> findBySpLike(String sp);

}

◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆

//MyController.java

package com.example.demo;

import java.util.List;

import java.util.Optional;

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

import org.springframework.stereotype.Controller;

import org.springframework.transaction.annotation.Transactional;

import org.springframework.web.bind.annotation.ModelAttribute;

import org.springframework.web.bind.annotation.PathVariable;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;

import org.springframework.web.bind.annotation.RequestParam;

import org.springframework.web.servlet.ModelAndView;

@Controller

public class MyController {

@Autowired

MyDataRepository repository;



//トップ

@RequestMapping("/")

public String index() {

    return "index";

}



//入力

@RequestMapping("/write")

public ModelAndView write(@ModelAttribute("formModel") MyData mydata,ModelAndView mv) {

    mv.setViewName("write");

    return mv;

}



@RequestMapping(value="/form", method=RequestMethod.POST)

@Transactional(readOnly=false)

public ModelAndView form(@ModelAttribute("formModel") MyData mydata, ModelAndView mv) {

    repository.saveAndFlush(mydata);

    return new ModelAndView("redirect:/");

}



//一覧

@RequestMapping(value="/list",method=RequestMethod.GET)

public ModelAndView list(ModelAndView mv) {

    mv.setViewName("list");

    List<MyData> list = repository.findAll();

    mv.addObject("datalist", list);

    return mv;

}



//詳細

@RequestMapping(value="/data/{id}",method=RequestMethod.GET)

public ModelAndView data(@ModelAttribute MyData mydata, @PathVariable int id, ModelAndView mv) {

    mv.setViewName("data");

    Optional<MyData> data = repository.findById(id);

    mv.addObject("formModel", data.get());

    return mv;

}



//削除

@RequestMapping(value="/delete/{id}",method=RequestMethod.GET)

public ModelAndView delete(@PathVariable int id, ModelAndView mv) {

    mv.setViewName("delete");

    Optional<MyData> data = repository.findById(id);

    mv.addObject("formModel", data.get());

    return mv;

}

@RequestMapping(value="/delete",method=RequestMethod.POST)

@Transactional(readOnly = false)

public ModelAndView remove(@RequestParam int id) {

    repository.deleteById(id);

    return new ModelAndView("redirect:/");

}



//検索

@RequestMapping(value="/search")

public ModelAndView search(@RequestParam("sp") String sp,ModelAndView mv) {

    List<MyData> list = repository.findBySpLike("%" + sp + "%");

    mv.setViewName("list");

    mv.addObject("dataList", list);

    return mv;

}

}

◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆

<!DOCTYPE html>

トップページ

旅行アプリ





◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆

<--search.html-->

<!DOCTYPE html>

検索画面

観光地など:

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

IntelliJでJava SpringBootのプロジェクトを作成

はじめに

IntelliJ IDEAでSpringBootのプロジェクトを作成する手順を整理します。
今回はJava、Gradleを利用します。

手順

プロジェクト作成

IntelliJを起動してCreate New Projectをクリックします。

スクリーンショット 2020-06-19 0.06.56.png

Gradle、Javaを選択してNextをクリックします。

スクリーンショット 2020-06-19 0.13.14.png

SprintBootTestとしてプロジェクトを作成します。

スクリーンショット 2020-06-19 1.49.36.png

build.gradleの編集

プロジェクトで作成されるbuild.gradle

build.gradle
plugins {
    id 'java'
}

group 'com.ykdevs'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

以下の様に編集。
バージョンはSpringBootから最新版を指定した。
IntelliJを利用するので、pluginsにideaを指定した。参考
JUnitはJUnit5を利用する。

build.gradle
buildscript {
    ext {
        springBootVersion = '2.3.1.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

plugins {
    id 'idea' // IntelliJ
    id 'java'
}

group 'com.ykdevs'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

dependencies {
    // SpringBoot
    implementation "org.springframework.boot:spring-boot-starter-thymeleaf:${springBootVersion}"
    implementation "org.springframework.boot:spring-boot-starter-web:${springBootVersion}"
    testImplementation "org.springframework.boot:spring-boot-starter-test:${springBootVersion}"

    // JUnit
    testImplementation 'org.junit.jupiter:junit-jupiter:5.6.2'
}

アプリケーション実行クラスの作成

プロジェクト作成時に以下のようなディレクトリができている。

src
├─ main
│  │
│  ├─ java
│  │
│  └─ resources
└─ test
   │
   ├─ java
   │
   └─ resources

main/java配下でパッケージの作成を行う。

スクリーンショット 2020-06-21 21.58.00.png

カンマ区切りでパッケージ名を指定すれば、ディレクトリが自動的に作成される。

スクリーンショット 2020-06-21 22.22.16.png

パッケージディレクトリでアプリケーションクラスファイルを作成する。

スクリーンショット 2020-06-21 22.05.29.png

@SpringBootApplicationのアノテーションをつけ、main関数を作成する。

Application.java
package com.ykdevs.springboottest;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

クラス名のところで右クリックでGenerateを選択し、Testを作成する。

スクリーンショット 2020-06-21 22.11.30.png

スクリーンショット 2020-06-21 22.11.40.png

テストは以下の感じ。

package com.ykdevs.springboottest;

import org.junit.jupiter.api.Test;

class ApplicationTest {

    @Test
    void main() {
        Application.main(new String[0]);
    }
}

Runでテスト実行

スクリーンショット 2020-06-21 22.29.59.png

スクリーンショット 2020-06-21 22.30.26.png

参考

Gradle User Guide
Spring Boot入門−公式ドキュメント
JUnit 5 ユーザーガイド

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

Spring Initializr を使って爆速でHello World!!!

目的

JavaのフレームワークであるSpringBootを用いて、
Hello Worldをとりあえず出力させたい!という方のためへ残しておきます。

Spring Initializrってなに?

ブラウザ上で、ビルドツールや開発言語を選択して、雛形となるアプリケーションを簡単に生成することが出来るもの。
(Ruby on Railsでいう所のscaffold(スキャフォールド)みたいなものと認識しています。)

実践編

今回扱う各種バージョン

OS:macOS Mojave バージョン10.14.6
テキストエディタ:Visual Studio Code(以下VSCode)

$ java -version
openjdk version "11.0.2" 2019-01-15
OpenJDK Runtime Environment 18.9 (build 11.0.2+9)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.2+9, mixed mode)
$ mvn -version
Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Maven home: /usr/local/Cellar/maven/3.6.3_1/libexec
Java version: 13.0.2, vendor: N/A, runtime: /usr/local/Cellar/openjdk/13.0.2+8_2/libexec/openjdk.jdk/Contents/Home
Default locale: ja_JP, platform encoding: UTF-8
OS name: "mac os x", version: "10.14.6", arch: "x86_64", family: "mac"
$ spring --version
Spring CLI v2.3.1.RELEASE

Spring Initializrの公式へGo!!!

こちら にアクセスしてください。

各種追加、修正を加えて以下の様にします。
スクリーンショット 2020-06-21 20.36.41.png

そして、GENERATEボタンを押下。

GENERATEボタンを押下したことにより、Zipファイルがダウンロードされたと思います。

そのZipファイルを展開してください。

テキストエディタで修正

スクリーンショット 2020-06-21 20.41.39.png

今回は、demoという名前のフォルダがあるはずですので、お使いのテキストエディタで開いてください(今回はVSCodeです)。

これ余談なんですけど、拡張機能で以下をインストールしておくことをおすすめします(作業が捗ります)。

・Java Extension Pack
・Spring Boot Tools
・Lombok Annotations Support for VS Code

src/main/java/com/example/demoの中に、HelloController.javaを作成します。

スクリーンショット 2020-06-21 21.16.45.png

SpringBootの公式のコードを参考にしつつ、
HelloController.javaの中身を記述していきましょう!

HelloController.java
package com.example.demo;
import org.springframework.boot.autoconfigure.*;
import org.springframework.web.bind.annotation.*;

@RestController
@EnableAutoConfiguration
public class HelloController {

  @RequestMapping("/")
    String home() {
        return "Hello World!";
    }
}

そして、ターミナルでdemoフォルダに移動して、
mvn spring-boot:runと入力してください。

ターミナル
$ mvn spring-boot:run

[INFO] Scanning for projects...
[INFO] 
[INFO] --------------------------< com.example:demo >--------------------------
[INFO] Building demo 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] >>> spring-boot-maven-plugin:2.3.1.RELEASE:run (default-cli) > test-compile @ demo >>>
[INFO] 
[INFO] --- maven-resources-plugin:3.1.0:resources (default-resources) @ demo ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.

〜省略〜

しばらくすると以下のように表示されます。

ターミナル
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.3.1.RELEASE)

〜省略〜

そして、ブラウザでhttp://localhost:8080/
を入力すると、

スクリーンショット 2020-06-21 21.23.37.png

Hello World!と表示されましたね!

終わりに

今回はSpringBoot等の詳しい説明は割愛させていただきました。
(ブラウザで表示出来るところまでをゴールにしました)

ご自身でコードを変更してはブラウザで確認するといった学習にご活用いただければと思います。

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

staticについて

クラスファイルはロード後、staticな部分とそれ以外に分離され、それぞれ異なるメモリ領域に保存されます。
static.png

staticで修飾されたメンバはstatic領域に配置され、それ以外の定義はピープ領域に配置されます。

  • インスタンスが生成される時には、ピープ領域にあるクラス定義に従ってインスタンスが生成される
  • staticなフィールドはインスタンスを作らなくても使える
  • staticなフィールドにアクセスするには「class名.フィールド名」と記述する。もしくはインスタンスを生成してその参照を使ってアクセスする。
  • staticなメンバはインスタンスがなくても使えるのに対し、staticではないメンバはインスタンスがないと使えない。よって、staticなメソッドからstaticではないメンバにアクセスすることはできない
  • 逆にstaticではないメソッドからstaticなメンバにアクセスすることは可能

参考文献

徹底攻略Java SE11 Silver問題集

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

AndroidのWebView#shouldInterceptRequestでリクエストをinterceptするときに忘れがちなこと

AndroidのWebViewでHTTPレスポンスのハンドリングやタイムアウトを独自に実装したいときに、shouldInterceptRequestをオーバーライドして、独自にHTTP通信を実装することがありますよね。

サンプルなので大雑把な実装ですが、例えば以下のような感じに実装したとしましょう。

override fun shouldInterceptRequest(view: WebView?, request: WebResourceRequest?): WebResourceResponse? {
    val latch = CountDownLatch(1)
    var res: InputStream? = null

    val call = createOkHttpClient().newCall(Request.Builder().url(request?.url.toString()).method("POST", RequestBody.create(null, "hoge")).build())
    call.enqueue(object: Callback {
        override fun onFailure(call: Call, e: IOException) {
            latch.countDown()
        }

        override fun onResponse(call: Call, response: Response) {
            res = response.body()?.byteStream()
            latch.countDown()
        }
    })

    latch.await()
    return WebResourceResponse("text/html", "UTF-8",res)
}

private val cookieStore = HashMap<String, MutableList<Cookie>>()

fun createOkHttpClient(): OkHttpClient {
    return OkHttpClient.Builder()
        .cookieJar(object: CookieJar {
            override fun saveFromResponse(url: HttpUrl, cookies: MutableList<Cookie>) {
                cookieStore[url.host()] = cookies
        }

            override fun loadForRequest(url: HttpUrl): MutableList<Cookie> {
                val cookies = cookieStore[url.host()]
                return cookies ?: ArrayList()
            }
        })
        .build()
}

でも、これを使うときは要注意。
例えば、以下のようなHTMLを取得する場合を考えてみましょう。

<html>                                                                                                                                                                                                                                        
<body>
<script type="text/javascript">
  function doPost() {
    document.TestForm.submit();
  }
</script>

<h1>Test</h1>

<form id="TestForm" name="TestForm" action="http://192.168.100.2:3000/hoge" method="post">
  <input type="hidden" name="hoge" value="hogeVal"/>
  <input type="hidden" name="fuga" value="fugaVal"/>
  <input type="submit" value="submit">
</form>
<script type="text/javascript">
  doPost();
</script>
</body>
</html>

WebViewはHTML解析機能を持っていますから、上記のHTML読み込み時に、doPost関数が呼び出され、

タグに定義されたhttp://192.168.100.2:3000/hogeに対して、自動的にリクエストを送信します。

このとき、shouldInterceptRequestでリソースの読み込みをWebViewに任せていれば(shouldInterceptRequestをオーバーライドしていなければ)、
<input>タグのhidden属性の値を自動的にPOSTしてくれますが、開発者が独自にOkHTTPなどでリクエストを実装する際は、HTMLの内容を解析して自前で付与しないといけません。(デフォルトではhidden属性の値は付与されない)

以下のようなサーバを立てて、ログを見てみましょう。

{-# LANGUAGE OverloadedStrings #-}                                                                                                                                                                                                            
module Main where

import Web.Scotty
import Control.Monad.IO.Class (liftIO)

main :: IO ()
main = scotty 3000 $ do
  post "/test.html" $ file "./static/test.html" >> setHeader "Content-Type" "text/html"
  post "/hoge" $ do
    (liftIO . putStrLn $ ("Access to /hoge. Headers: " :: String)) >> headers >>= liftIO . print
    (liftIO . putStrLn $ ("Params: " :: String)) >> params >>= liftIO . print
    text "hoge success"
shouldInterceptRequestをオーバーライドしない場合
Access to /hoge. Headers: 
[("Host","192.168.100.151:3000"),("Connection","keep-alive"),("Content-Length","25"),("Cache-Control","max-age=0"),("Origin","http://192.168.100.151:3000"),("Upgrade-Insecure-Requests","1"),("Content-Type","application/x-www-form-urlencoded"),("User-Agent","Mozilla/5.0 (Linux; Android 9; Android SDK built for x86 Build/PSR1.180720.012; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.158 Mobile Safari/537.36"),("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"),("Referer","http://192.168.100.151:3000/test.html"),("Accept-Encoding","gzip, deflate"),("Accept-Language","ja-JP,en-US;q=0.9"),("X-Requested-With","com.badlogic.masaki.webviewjsinterfacesample")]
Params: 
[("hoge","hogeVal"),("fuga","fugaVal")]
shouldInterceptを上記実装でオーバーライドした場合
Access to /hoge. Headers: 
[("Content-Length","4"),("Host","192.168.100.151:3000"),("Connection","Keep-Alive"),("Accept-Encoding","gzip"),("User-Agent","okhttp/3.10.0")]
Params: 
[]

HTMLのhidden属性に定義されていた内容がPOSTされていませんね。これだとサーバから意図したレスポンスが返却されなくなってしまうでしょう。
その他、User-Agentなどのヘッダー情報も代わってしまいますから、shouldInterceptRequestをオーバライドするときは、この点も注意。

hidden属性の値をPOSTするためには、HTMLの解析が必要になってきます。
ここら辺は既に情報が色々出回っています。

HTMLの内容の取得方法は以下とか参考になりました。
https://stackoverflow.com/questions/8200945/how-to-get-html-content-from-a-webview

OkHttpで、application/x-www-form-urlencodedのPOSTパラメータを設定する方法は以下とかが
参考になりました。
https://stackoverflow.com/questions/35756264/how-do-i-post-data-using-okhttp-library-with-content-type-x-www-form-urlencoded

以前の記事でも同じようなこと言いましたが、WebViewの目的はHTMLのレンダリングだから、WebAPI呼び出しに使うようなものではないですね。

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

【Java】インスタンス変数とクラス変数の違いについて

目的

インスタンス変数とクラス変数の違いについて理解するため

検証環境

  • Eclipse Oxygen.3a Release (4.7.3a)
  • Java8

検証用ソースコード

1.クラス変数とインスタンス変数

public class Oimo {
    public static int CNT_CLASS; // クラス変数
    public int CNT_INSTANCE; // インスタンス変数
}

2.クラス変数を試す

public class Kensho01 {

    public static void main(String[] args) {
        // クラス変数
        Oimo oimo3 = new Oimo();
        Oimo oimo4 = new Oimo();

        oimo3.CNT_CLASS = 30;

        System.out.println(oimo4.CNT_CLASS); // 30が出力される
    }
}

3.インスタンス変数を試す

public class Kensho01 {

    public static void main(String[] args) {
        // インスタンス変数
        Oimo oimo1 = new Oimo();
        Oimo oimo2 = new Oimo();

        oimo1.CNT_INSTANCE = 10;

        System.out.println(oimo2.CNT_INSTANCE); // 0が出力される
    }
}

実施手順

  • クラス変数とインスタンス変数を持ったOimoクラスを用意。2つのOimoインスタンスを生成し、1つ目のインスタンスのクラス変数およびインスタンス変数に対して値を設定後、2つ目のOimoインスタンスに設定されているクラス変数およびインスタンス変数の値を確認する。

実施結果

  • 1つ目のインスタンスのクラス変数に30を設定したところ、2つ目のインスタンスのクラス変数にも30が設定された。
  • 1つ目のインスタンスのインスタンス変数に10を設定したところ、2つ目のインスタンスのインスタンス変数には0が設定された。

考察

インスタンス変数はインスタンス毎に参照する変数で、クラス変数は複数インスタンスが共通で参照する変数ということが分かった。スレッドセーフなプログラムを組もうと思ったら、この知識は大切なことが分かる。

ではまた(^_^)ノシ

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

Person.java 引数にVehicle型の値を受け取るbuyメソッドを新たに定義する。

class Person {
private String firstName;
private String middleName;
private String lastName;
private int age;
private double height;
private double weight;

Person(String firstName, String lastName, int age, double height, double weight) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
this.height = height;
this.weight = weight;
}

Person(String firstName, String middleName, String lastName, int age, double height, double weight) {
this(firstName, lastName, age, height, weight);
this.middleName = middleName;
}

public String fullName() {
if (this.middleName == null) {
return this.firstName + " " + this.lastName;
} else {
return this.firstName + " " + this.middleName + " " + this.lastName;
}
}

public void printData() {
System.out.println("名前は" + this.fullName() + "です");
System.out.println("年齢は" + this.age + "歳です");
System.out.println("身長は" + this.height + "mです");
System.out.println("体重は" + this.weight + "kgです");
System.out.println("BMIは" + Math.round(this.bmi()) + "です");
}

public double bmi() {
return this.weight / this.height / this.height;
}

// 以下2つを一つのメソッドで書き換えてください
public void buy(Vehicle vehicle){
vehicle.setOwner(this);
}
}

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

我流プログラミング学習8選

期せずしてITを生業とするようになってしまいました。
今でこそITプロダクトのPMをやっているけれども、当初は「未経験」ながらもあらゆることをキャッチアップせねばなりませんでした。

「文系/未経験」が技術を理解し身につけていくファーストステップは、開発案件にアサインされてフィードバックを得ていくパターンが王道かと思います。
自分の場合は、周囲に頼れる人/情報が多くなかったため試行錯誤の末プログラミングを身に着け、今に至ります。
本記事は、そんなかなり我流のプログラミング学習を振り返ることで、なんらかの参考になれば幸いです。

ちなみに、自分は仕事ではJavaを使い、プライベートではKotlin、JavaScriptを使うことが多いです。
以下、断りがなければJavaのことを暗に言っているんだと思ってください。

記事の最後に学習に使用した書籍やサイトのリファレンスをまとめたので、よろしければそちらもご覧ください。

学習履歴から

挙げたらきりがないくらい、なにかを作ったり教材、リファレンスにあたってきましたがその中でも特に学習効果が高かったと感じるものを8つ、挙げてみました。
1. 簡単なウェブアプリを決めて制作する
2. Google Tech Dev Guide
3. 壮大な「趣味の開発」と派生プロジェクト
4. 開発パートナーのソースコード読み漁る
5. 数学
6. C言語
7. プログラミング言語の標準APIを掘る
8. AWS環境の構築

なお、私もはじめはProgateなどとっかかりは別でこなしてはおりまして、そこからのステップアップの方法が主題となります。

1. 簡単なウェブアプリを決めて制作する

超最初期の学習法。
「プログラミングスクールやオンライン教材で文法は理解できたけど次どうしよう」のステップでは、自分で作りたいものを決めて開発するのがとてもいいと思います。
結構どんなのを作ってもいいんですが、1.のエッセンスは「成果物から逆算して必要な概念やロジックを理解する」ことです。

たとえば、LINE Bot開発は超初期の「初心者」レベルでなにかを作ってみたいときはめちゃくちゃおすすめしたいです。
これは単純に、「LINEから入力を受け取って処理をするスクリプトを作ってみる」。

おすすめ理由は大きく3つで、

  • インプットからアウトプットまでのロジックが簡単
  • Rubyのようなスクリプト言語で作れてしまう
  • 簡単なわりに結構いい感じのものができて、ちょっと達成感を得られる

特に3つめが大事なのかなと。結構簡単に作れる割には達成感を得やすい。
Google Apps Script(GASと呼ばれているやつ)とLINE Botの組み合わせなんかは、ちょっとできるようになってからでも十分楽しめますよね。
ネットで検索すればいろいろな実装例が出てきます。
スプレッドシートを簡易DBに見立てて、「LINEからの入力を受けてデータを返す」みたいな構成にすると、「UI - ロジック - DB」の基盤作りもでき、多くのことを学べます。

2. Google Tech Dev Guide

Google Tech Dev Guideは一時期やりこんでました。

自分の理解では、Google Tech Dev GuideはGoogleがキュレーションしているテーマ別のプログラミング教材サイトです。
目的やレベル別になっていて、ビギナーでも取り組みやすいです。

この教材は、1をクリアしてから挑むと難易度がちょうどいいじゃないかな。
なるほどと思えるくらいしっかりした内容なのに、ビギナーでも一定頭を使えば取り組める教材が揃っている。
ひとしきりやったら、ちょっとしたアルゴリズムを自作できるくらいにはなりました。

「1. 簡単なウェブアプリを決めて制作する」は、「なにか出来上がる楽しみ」はある反面、何を作るかを頻繁に考えないといけないデメリットがあります。2.のようにコースが決まっていると端から進めていきやすいので学習に集中しやすい側面があると考えてます。

3. 壮大な「趣味の開発」と、派生プロジェクト

壮大な「趣味の開発」プロジェクト。
これはもう、本当に壮大で「普通にあったら使うなっていうのをSPAで作る」ってことをやってました。

足掛け1年弱、主に土日を使ってずっと作ってた...
フロントとバックエンドを、盛大に切り離して2つのプロジェクトを同時進行させてました。
フロント側のプロジェクトとバックエンド側のプロジェクトを平行して進めたため、体感では3倍くらいのコード量になってた気がします。
フロント〜ロジック〜DBまでを一貫して設計〜リリースしたいっていうのがこのときのモチベーションで、仕事ばりに設計書を書いて(笑)、実装してテストまで一貫してやりきった。

このプロジェクトもめちゃくちゃ得ることが多かったので、まじでやってよかった。
よかったポイントを挙げていくと、

  • 結構本気で書いたので、コードベースがあとあと他のところでも使い回せるくらい洗練されてた
  • e2eで確かに思い描いていたものを作り上げられた
  • デプロイ基盤、サーバ/ネットワーク構成も自作して、文字通り「システムを立ち上げた」
  • ここで書いたソースコードを一部抜き出してちょっとしたライブラリ化できた
  • いい感じのものを作り上げた達成感
  • 世の中のサービスの表と裏、上から下までの構造をなんとなく想像できるようになる

などなど。他にももう少しありそう。

このプロジェクトはまあ大量のコードを生み出したので、その一部を切り出して自分のよく使うライブラリとして現在もメンテしてます。
そのような派生して生み出されたソースも資産としてのこって行くので、思い出深いです。

4. 開発パートナーのソースコード読み漁る

私の会社では(厳密にいうと)エンジニアのロールが存在せず、ITの実働は常駐していただいているパートナーにお願いしてます。
そして、事業で動いているソースコードはすべて社内のバージョン管理システムに置かれて、いつでも誰でも読める状態になっています。
一時期、まあ飽きることなくいろいろなプロジェクトのソースを読むことで学びを得ており、ここに挙げてみました。

この学習方法は、少しコツのいる作業かもしれないです。
人のソースコードは、読んでるだけだと全然よくわからない。本当によくわからなかった...
他人の積み上げたソースを、腰を据えて読む段階では何個か気をつけるとよい点があるので、列挙してみます。

気をつけたこと1 「知らないクラスを徹底的に検索する」。

びっくりするぐらい聞いたこともないようなクラスが、標準ライブラリだったり依存ライブラリでたくさん出てくる。
上からソースコードを読んで、わからないクラスは「Java 〇〇」(〇〇はクラス名)でひたすら検索してました。
ここでは、公式ドキュメントにあたり続けるのがよいですね。
公式ドキュメントは省略こそ少ないし初見ではわかりづらいこともあるけれども、過不足ない知識を提供してくれます。今後、他のなにかを始めるときに「公式ドキュメント解読力」は自分の力になります。
本当、ずっとJavadoc読んでました...

気をつけたこと2 「呼び出し先のクラス/メソッド/プロパティを追いかけ続ける」

これもめちゃくちゃやった...人のソース読んでも、そもそもそのコードが書かれた文脈がわからない。
ソースの積み上げられた文脈はソースからだけではあまりわからず、
「お前(知らないクラス、メソッド、プロパティ)誰だよ...」
ってよくなってました。
当時、私は「ラップされすぎてて実体がわからねえ」苦しみに悶てました...
とりあえず呼び出し先のクラスを複数回、追いかけて具体的な実装が出てくるところまで遡るとよいと思います。
体感では、「よくわからない構造」は4〜5回くらい呼び出しや実装/拡張を経ている印象です。

でも2はいまだにベストプラクティスわからないです、今でも苦労してます。

気をつけたこと3 「すべてが"実装例"だと理解する」

プロジェクトによっては、イケてるロジック・構造とそうじゃないところの差がすごい大きいんですよね。

ビギナーのうちは、結局どういうロジック/構造がよいのか、判断つかなくてよく混乱してました。
似たようなことをやりたいのに、ある箇所と別の箇所ではまるでロジックが違うのはよくある。

個人的な印象では、よく知られたフレームワークやライブラリの実装でもない限り「超王道」を貫いているプロジェクトは少ないと思ってまして、その意味で「すべてのソースは実装例である」くらいに理解しておくことにしてます。
本番で動いてるソースの全部が全部、王道に沿って構成されてないという(笑)

そして、そりゃそうなんすよね。そんなに全部が全部超かっこよく構成してないし(いろいろな制約があって)できない。

5. 数学

他の学習方法と比べても、とりわけ価値の高い情報を摂取できたのが数学の勉強でした。
「ちょっとしたウェブアプリは、調べながらできるようになってきたな」くらいで着手して、学習効率がとても高かった方法です。
列挙した8つの学習方法の中でも、とりわけ学習効果が高かったです。

情報系を卒業された方であれば数学が大事なのは当たり前すぎて論を俟たないですが、そりゃそうやんって話で、そもそも(一般的な)コンピュータそのものが離散数学のアイデアをベースに設計されていますよね。
だから、コンピュータを使って仕事していく上で数学は欠かせないのかなと思っています。

やったこととしては、座学を中心に学部生向けの情報系の学生がやるようなテキストやウェブ上の資料を何冊かこなしました。
ここでもいろいろやりましたが、何個か例をあげると

カリキュラム、というかどの領域を学ぶか?は応用情報技術者試験の出題範囲などを参考に選定。

このときは家に帰ってから毎日何時間か、数カ月間帰ってテキスト開いて数学の問題を解く生活をやっておりました(笑)
学生時代にも線形代数や簡単な微分積分は使っていたので数学自体は苦手をあまり感じず、それはめっちゃラッキーでした。
必要性を感じて勉強をしたけど、座学そのものが純粋に面白かった。
今まで触れてこなかった領域だったので、離散数学は独特の匂いを感じられて大変興味深かったです。

私はなにを扱うにも原理が気になる性でして、プログラミングでも「それはなぜ?」を追いかけ続けたくなってしまう。
数学はプログラミングにおいて「なぜそうなのか?」を自己解決していくための超重要ツールであることを(ようやく)ここで知り、テキストをこなしてるときは毎日、興奮しっぱなしでした。
数学で扱う構造をクラスで表現しようとするとこういう構成になるんだ、ってのを一つひとつ原理的な部分から理解できた。

いっぽうで、「学校のお勉強」感満載でして、腰を据えてやらないと途中で楽しくなくなる可能性が高いかもしれません...
万人にはおすすめできないです。

6. C言語

王道中の王道なので、「C言語超いいですよ!」って大声で列挙するのはおかしな話なんですけど...
よく言われるようにC言語は他の多くの言語に影響を与えてきてますし、一回くらいはやってもいいかなって。

何かを作り、動くのを目的にプログラミングする場合は、他の言語やツールを使うほうが楽なケースが多いと思ってまして、だからC言語の学習は「ちゃんと定義して、ちゃんと動作させる」ことそのものを目的に学習しました。
C言語は、5の数学と前後して一緒に勉強していて「原理を知る」ことをそのものが狙いでした。
そのため、学習サイトのサンプルを見ながら軽めに書いてコンパイルして動かす程度の深さでしか触れてないです。

あんまり深追いはできてないものの、アルゴリズム・データ構造の実装もC言語と平行してやるようにはしてました。

よく「つまづきやすい」と言われているポインタ周りは、ご多分に漏れ、ず私も「これは深いゾ?!」と途中で気づきました。
ポインタ周りだけは別途書籍で学習、オライリーのがよかったです。

この本はいまだにちょくちょく参照してます。人間から見える「オモテ面」と、物理的な動作の「ウラ面」をほどよく抽象化して説明してくれている点で大変参考になりました。
メモリ管理のくだりは特にお気に入りです。

7. プログラミング言語の標準APIを掘る

なんらかプログラミング言語の標準APIは、都度参照していくとよいと思います。
自分の場合は、仕事ではJavaを使い、プライベートではKotlinを触ることが多いのでJDKのソースはちょくちょく読んでます。

慣れちゃえば「標準APIとか、ライブラリのソース読むなんて当たり前じゃね」と思うようになるのかもしれないんですが、個人的な感想として「本家にあたる」のはハードルの高い作業だと感じます。

「本家のやっていることを直に見られる」意味でこれをやるといいと思ってます。逆にいえばこれ以外で得られることはは小さいかも。
なにか標準APIのクラスやメソッドを使うときに、より納得して使えるようになるし誤った用法を避けられるのも、副作用にはあるかもしれないですが。
用法はドキュメントを読めば解消できますし。

ちなみに、7.は「4. 開発パートナーのソースコード読み漁る」に近いです。
興味のある、ないしはもっと理解を深めたいクラスやメソッドを探し出してきて「これは何してるんだ?」と掘っていくだけ。
「4. 開発パートナーのソースコード読み漁る」との違いは、標準APIのほうが抽象的だったりクラス構成が重厚だったりして追いかけづらい点などでしょうか。

8. AWS環境の構築

アプリだけでなく「アプリ基盤も自分でこさえてみようぜ」って項目です。
AWSでなくAzureでもGCPでも、好きなIaaSを選んだらいいです。なんならIaaSじゃなくてもいいですし、「ネットワークとサーバ」を簡単に立ち上げられる環境を用意できればいいです。

昔、インフラチームとアプリチームを兼務していた時期あり、サーバを組み立ててミドル入れて、みたいな仕事をしていたので業務の補完として取り組んでました。
「3. 壮大な「趣味の開発」と、派生プロジェクト」で作った、壮大な趣味アプリをデプロイする基盤も自分で作り、運営できるところまでは構築しました。
ネットワークを構成して、その中にサーバを置いてミドル入れてアプリを配備してって。

(自分が言うことでもないけれども)AWSは、ドキュメントやチュートリアルがまー充実してるのでドキュメントを読んで組み立てるんでも十分いろいろできるかと思います。

自分は、仕事でAWSに入ったのでチュートリアルを知らず、自分のアカウント開設してから「こんなパワフルなチュートリアルあったんか...」と驚きました。
これほんとうにすごいですよね...読んでると動かしたくなってきてしまう。
チュートリアルに出てくるテーマのうち何個かをやってみて、説明ややりたいことがわかりやすく、簡単にできて普通に楽しかったです(笑)

アプリ開発の観点では、VPCやサブネットをチュートリアルなどを見ながら自前で用意して、そのネットワーク内にサーバを立てて稼働基盤を組み立てる一連の流れを踏んでみるのが一番オススメです。

いまでもPMしながらアーキテクチャのディスカッションに入ってて、わからないところは自分のアカウントでちょっとしたデモを立ち上げて勉強するのにとても役立ってます。

最後に

学習方法はもちろん大事でいろいろ試行錯誤すべきと思っている反面、「好奇心」がない限りはなかなか体になじんでいかないですよね。
これを書きながらも、Howと同じかそれ以上にWhyを考えてこんでしまうか?ってのは大きな論点なんじゃないかと。
周囲の人を見ていても、普段からなんらかプログラムを作ったり勉強している人は義務感とか必要性以上に好奇心がそうさせているように感じられてならないです。
そして、私は業務でたまたま触る機会と必要性があったから触れることができたけども、ここまでのめり込めるとは思いもよらなかった。

どんなものでもよいので、興味を持てる技術を深堀りするところから始めてみてるのもよいのかもしれません。

参考

本文に出てきたもの、出てきてないが参考になったものです。

1.簡単なウェブアプリを決めて制作する

2.Google Tech Dev Guide

3.壮大な「趣味の開発」と派生プロジェクト

5.数学

6.C言語

7.プログラミング言語の標準APIを掘る

8.AWS環境の構築

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

Car.java

class Car{
private String name;
private String color;
private int distance = 0;
private int fuel = 100;
Car(String name,String color){
this.name = name;
this.color = color;
}

public void printData(){
System.out.println("名前:"+this.name);
System.out.println("色:"+ this.color);
System.out.println("走行距離:"+this.distance+"km");
System.out.println("ガソリン量:"+this.fuel+"L");
}
public void run(int distance){
System.out.println(distance+"km走ります");
if(distance <= this.fuel){
this.distance += distance;
this.fuel -= distance;
}else{
System.out.println("ガソリンが足りません");
}

System.out.println("走行距離:"+this.distance+"km");
System.out.println("ガソリン量:"+this.fuel+"L");

}
public void charge(int litre){
System.out.println(litre+"L給油します");
if(litre <=0){
System.out.println("給油できません");
}else if(litre >=100){
System.out.println("満タンまで給油します");
this.fuel = 100;
}else{
this.fuel += litre;
}
System.out.println("ガソリン量:"+this.fuel+"L");
}
}

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

Main.java 自転車 車のガソリン残量 給油

import java.util.Scanner;

class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
Bicycle bicycle = new Bicycle("ビアンキ","緑");
System.out.println("【自転車の情報】");
bicycle.printData();
System.out.println("-----------------");
System.out.print("走る距離を入力してください:");
int bicycleDistance = scanner.nextInt();
bicycle.run(bicycleDistance);
System.out.println("=================");
Car car = new Car("フェラーリ","赤");
System.out.println("【車の情報】");
car.printData();
System.out.println("-----------------");
System.out.print("走る距離を入力してください:");
int carDistance = scanner.nextInt();
car.run(carDistance);
System.out.println("-----------------");
System.out.print("給油する量を入力してください:");
int litre = scanner.nextInt();
car.charge(litre);

}
}

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

Person.java

class Person {
private static int count = 0;
private String firstName;
private String middleName;
private String lastName;
private int age;
private double height;
private double weight;
// インスタンスフィールド「job」を追加してください
private String job;

// コンストラクタを書き換えてください
Person(String firstName, String lastName, int age, double height, double weight,String job) {
Person.count++;
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
this.height = height;
this.weight = weight;
this.job = job;
}

// コンストラクタを書き換えてください
Person(String firstName, String middleName, String lastName, int age, double height, double weight,String job) {
this(firstName, lastName, age, height, weight,job);
this.middleName = middleName;
}

public String getMiddleName() {
return this.middleName;
}

// jobのゲッターを定義してください
public String getJob(){
return this.job;
}

public void setMiddleName(String middleName) {
this.middleName = middleName;
}

// jobのセッターを定義してください
public void setJob(String job){
this.job = job;
}

public String fullName() {
if (this.middleName == null) {
return this.firstName + " " + this.lastName;
} else {
return this.firstName + " " + this.middleName + " " + this.lastName;
}
}

public void printData() {
System.out.println("私の名前は" + this.fullName() + "です");
System.out.println("年齢は" + this.age + "歳です");
System.out.println("BMIは" + Math.round(this.bmi()) + "です");
// 「仕事は◯◯です」と出力してください
System.out.println("仕事は"+this.job+"です");

}

public double bmi() {
return this.weight / this.height / this.height;
}

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

Main.java

class Main {
public static void main(String[] args) {
// 仕事を「医者」にしてください
Person person1 = new Person("Kate", "Jones", 27, 1.6, 50.0,"医者");
person1.printData();
// 仕事を「教師」にしてください
Person person2 = new Person("John", "Christopher", "Smith", 65, 1.75, 80.0,"教師");
person2.printData();
System.out.println("----------------------");
// person1の仕事を「獣医」に変更してください
person1.setJob("獣医");

// 「person1の仕事を◯◯に変更しました」と出力されるようにしてください
System.out.println("person1の仕事を"+person1.getJob()+"に変更しました");

person1.printData();

}
}

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