本日のJavaの積み上げ2

オブジェクト指向はリアルの行動に置き換えて考える。

例えばカートに商品を入れるという行為をソースコードに落とすと

ShoppingCartクラス内にaddProductメソッド(商品をカートに入れるメソッド)とproductListという配列(カート)を作る。

addProductメソッドを呼び出すことで商品が配列に格納されるというコードを書くとそのクラス内の流れが実際の行動として想像できる。

 

②クラス型のフィールドとインスタンス生成(初期化)という概念。

クラス型のフィールドを作る→クラスを犬に例えると、犬が持つ元々の特徴がフィールドとなる。

インスタンス作成→犬が新たに特徴を得る。

というイメージを忘れない。

 

③ローカル変数とクラス変数のスコープには注意する。

 

④配列のArrayListはクラス名をクラス型として使用可能。

ArrayList<Product> productList = new ArrayList<>()

のようにProductクラスを型として使えることを覚えておく。

 

 

Java 本日の積み上げ

Javaの配列を使うに当たり、

ArrayList<String> bar = (ArrayList<String>)list.clone();

のようにクローンした配列は

bar.add("犬");

のように要素を追加することが出来なかった。

(通常の配列にはもちろん追加可能)

 

② booleanを戻り値にしたメソッドを作成する際、

A == B 

だと何故かエラーが出るので

A.equals(B) 

のようにするとエラーが消えた。

 

③セッターやゲッターなどは一旦忘れてただのメソッドとして認識すると理解が進んだ。

 

④(mac) commandを押したまま引数などの要素をクリックすると、その要素とコネクトしている要素の場所へ飛んでくれる

 

⑤(mac)optionを押したまま引数などの要素をクリックしエンターを押すと、その要素から作れるメソッドなどがを自動で作ってくれるコマンドが出る

 

⑥メソッドの戻り値がreturnで返されるときは呼び出したメソッドがreturnの中身と入れ替わる

 

ということで本日も学びの多い一日だった。

Java インスタンスフィールドがややこしい

メソッド内でインスタンスフィールドにアクセスしたりと、ファイル間を言ったり来たりするのがわかりにくい。

最初から一つ一つ整理して見ていく。

 

インスタンスを生成し、変数に代入

Main.java

class Main {
public static void main(String[] args) {
 Vehicle vehicle = new Vehicle();
}

黄色Vehicle部:クラス型

ピンクvehicle部:変数名

青部:インスタンス生成

 

Vehicle.java

class Vehicle {

}

 

 

インスタンスメソッドの定義と呼び出し

Main.java

class Main {
public static void main(String[] args) {
 Vehicle vehicle = new Vehicle();
vehicle.run();
}

 

青部:インスタンス名.メソッド名() ;

これでメソッド呼び出している

 

Vehicle.java

class Vehicle {
public void run() {
System.out.println("速い");
}
}

緑void:戻り値の型

紫run部:メソッド名

 

インスタンスフィールドの定義

tMain.java

class Main {
public static void main(String[] args) {
 Vehicle vehicle = new Vehicle();
vehicle.name = "road star";
vehicle.run();
}

 ピンク部:インスタンス名.フィールド名 = 〇〇 でnameフィールドへ値をセットしている

 

Vehicle.java

class Vehicle {
public String name;
publi
c void run() {
System.out.println("速い");
}
}

 

青部:インスタンスフィールド を定義

(String name は名前を入れる変数を定義している)

 

④クラスの中でインスタンスを使う

Main.java

class Main {
public static void main(String[] args) {
 Vehicle vehicle = new Vehicle();
vehicle.name = "road star";
vehicle.run();
}

 

Vehicle.java

class Vehicle {
public String name;
publi
c void run() {
System.out.println(this.name + "は速い");
}
}

 

ピンク部:「this」という特殊変数を使う

runメソッド内でnameのインスタンスフィールドにアクセスするためにこの「this」という特殊変数を使い、これはクラス内のメソッドの定義の中でのみ使用可能。
thisはメソッドが呼ばれた時に、そのメソッドを呼び出しているインスタンスに置き換えられる…(これがちょっとややこしく感じる)

 

実行結果として

road starは速い

と出る。

 

ところでインスタンスフィールドが増えると毎回値をセットしないといけないので手間がかかる。
ここでインスタンスフィールドへ値をセットする楽な方法がコンストラクタとなっている。

 

⑤コンストラクタの定義

コンストラクタ:newを使ってインスタンス生成した後に自動で呼び出されるメソッド。Rails で言うところのinitializeメソッドみたいなもの?

Main.java

class Main {
public static void main(String[] args) {
 Vehicle vehicle = new Vehicle("road star");
vehicle.run();
}

ピンク部:インスタンスを生成するときにインスタンスフィールドへセットする値"road star"を引数として渡している

 

Vehicle.java

class Vehicle {
public String name;
Vehicle(String name) {
this.name = name;
}
publi
c void run() {
System.out.println(this.name + "は速い");
}
}

 

カラー部:コンストラクタを定義している

青Vehicle部:クラス名

黄色(String name)部:文字のnameを引数として受け取る

紫this.name = name部:nameインスタンスフィールドにthisメソッドで引数を受け取って値をセットしている

 

 

ということで一つ一つ見ると何となく分かるが、いきなりコードを見ても中々理解できない。

一つ一つと丁寧に見ていこう

Java メソッドの定義

public static 戻り値のデータ型 メソッド名(引数) {

  return 戻り値 ;

}

 

基本的な形は上記の通り。

ただややこしいのが、オーバーロードといっての型や個数が違う場合は同名のメソッドを定義できること。

実際に現場で同名のメソッドを定義することはないのかもしれないけれど、Progateで学習したらややこしくて頭が混乱した。

 

そしてメソッドが更にメソッドを呼ぶパターン。

 

 

Main.java

1
//実行部のMainメソッド
class Main {
 public static void main(String[] args) {
  printData(fullName("Tarou", "Nihon"), 27);
 }
//printDataメソッド
 public static void printData(String name, int age) {
  System.out.println("私の名前は" + name + "です");
  System.out.println("年齢は" + age + "歳です");
  }
 }
//fullName メソッド
 public static String fullName(String firstName, String lastName) {
  return firstName + " " + lastName;
 }
}

 

じっくり考えればわかるのだけれど、更に量が増えて複雑化すると???となってしまう。

 

これをMainメソッドの実行部とロジック部分を切り離すと少しだけわかりやすくなる。

Main.java

1
class Main {
 public static void main(String[] args) {
  Person.printData(Person.fullName("Tarou", "Nihon"), 27);
 }

 

Person.java

1
//新しいPersonクラス
class Person

//printDataメソッド
 public static void printData(String name, int age) {
  System.out.println("私の名前は" + name + "です");
  System.out.println("年齢は" + age + "歳です");
  }
 }
//fullName メソッド
 public static String fullName(String firstName, String lastName) {
  return firstName + " " + lastName;
 }

}

 

 

記述量が多い言語とのことなので一つ一つ確実にやっていきたい。

 

 

Mysql2::Error: Cannot drop index

マイグレーションファイルををロールバックしようとしたら…

 

remove_index(:tickets, {:column=>[:event_id, :user_id]})
rails aborted!
StandardError: An error has occurred, all later migrations canceled:

Mysql2::Error: Cannot drop index 'index_tickets_on_event_id_and_user_id': needed in a foreign key constraint

 

外部キー制約に必要な'index_tickets_on_event_id_and_user_id' を削除できないといわれている?

 

remove_indexと言われているのでロールバックしたいマイグレーションファイルに記載の下記をコメントアウトしてみるとロールバックできた。

 

# add_index :tickets, %i[event_id user_id], unique: true

 

 

よくわからないけれど外部キー制約はこんな事が起こるようだ。

Active Storage:画像ファイルのバリデーション

最近まで知らなかったが、Active Storage にはバリデーション用のgem があった。

 

gem 'active_storage_validations'

 

それまではgem を使わずにバリデーションを作っていたので、こんな感じだった。

 

models/application_recode.rb

class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true

ALLOWED_CONTENT_TYPES = %q{
image/jpeg
image/jpg
image/png
}
end

 

models/item.rb

class Item < ApplicationRecord

validates :image, presence: true
validate :check_image

private

def check_image
if image.respond_to?(:content_type)
unless image.content_type.in?(ALLOWED_CONTENT_TYPES)
errors.add(:image, "jpg, jpeg,pngのみアップロードできます")
end
if image.byte_size > 1.megabytes
errors.add(:image, "1MBまでアップロードできます")
end
end
end
end

 

 

 

active_storage_validations をbundle install してあげると

 

models/item.rb

validates :image, presence: true 
content_type: [:png, :jpg, :jpeg],
size: { less_than_or_equal_to: 1.megabytes },
dimension: { width: { max: 2000 }, height: { max: 2000 }}

 

これでできあがり。(エラー文は日本語にしてないけれど…)

ちなみにサイズに関しても上記のようにできる。

 

springって何?

テストコードにてfactory_botを実装し、rails console で確認したところ、

KeyError: Factory not registered: "user"

が出てきた。

こちらの記事を見るとどうやら"spring"が邪魔をしているらしい。

https://ja.stackoverflow.com/questions/65296/factorybot%E3%81%8C%E5%8B%95%E4%BD%9C%E3%81%97%E3%81%BE%E3%81%9B%E3%82%93

 

spring stop で停止すると問題なくrails console でfactory_bot が動き、レコードが作成できた。

 

[2] pry(main)> FactoryBot.create(:user)

KeyError: Factory not registered: "user"

from /Users/tak/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.4/lib/active_support/hash_with_indifferent_access.rb:191:in `fetch'

Caused by KeyError: key not found: "user"

from /Users/tak/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.4/lib/active_support/hash_with_indifferent_access.rb:191:in `fetch'

 

正直このエラーのどこにspring 要素があるのか不明だが、今後コンソールで止まった時はspring を一度停止してみるようにする。

 

 

ちなみに下記のようにspring は確認するとまた動いていた。

tak@MacBook-Air go-events % spring status

Spring is running:

 

12601 spring server | go-events | started 9 mins ago   

12602 ruby -I /Users/tak/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bundler-2.1.4/lib -I /Users/tak/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/spring-2.1.1/lib -e require 'spring/application/boot'

 

 

ところで"spring"って何?

 

こちらの記事を見たところ

https://pleiades.io/help/ruby/spring.html

アプリケーションをバックグラウンドで実行し続けることによって開発をスピードアップする Rails アプリケーションプリローダーです。

とのこと。

bin/rails コマンドの2回目以降の起動時間が短縮されるということらしい。

 

この記事には

Test::Unit / Shoulda / Minitest テストは、デフォルトで Spring を使用せずに実行されることに注意してください。

と書いてあるからspring 止めなきゃいけなかったのかな?

 

とりあえずなんとなくわかった…ということにしておこう。